From 5e377965b01ccc661fe6b50813121201a7aae9e5 Mon Sep 17 00:00:00 2001 From: Ulrich Huber Date: Tue, 14 Apr 2026 10:11:15 +0200 Subject: [PATCH 01/10] Add sphinx translator for lobster --- tools/sphinx/BUILD | 1 + tools/sphinx/extensions/BUILD | 0 tools/sphinx/extensions/lobster/BUILD | 21 +++ tools/sphinx/extensions/lobster/README.md | 47 +++++ tools/sphinx/extensions/lobster/lobster.py | 202 +++++++++++++++++++++ 5 files changed, 271 insertions(+) create mode 100644 tools/sphinx/extensions/BUILD create mode 100644 tools/sphinx/extensions/lobster/BUILD create mode 100644 tools/sphinx/extensions/lobster/README.md create mode 100644 tools/sphinx/extensions/lobster/lobster.py diff --git a/tools/sphinx/BUILD b/tools/sphinx/BUILD index d1a64d03..6abb006d 100644 --- a/tools/sphinx/BUILD +++ b/tools/sphinx/BUILD @@ -14,6 +14,7 @@ load("@pip_rules_score//:requirements.bzl", "requirement") load("@rules_java//java:defs.bzl", "java_binary") load("@rules_python//sphinxdocs:sphinx.bzl", "sphinx_build_binary") +load("@pip_tooling//:requirements.bzl", "requirement") java_binary( name = "plantuml", diff --git a/tools/sphinx/extensions/BUILD b/tools/sphinx/extensions/BUILD new file mode 100644 index 00000000..e69de29b diff --git a/tools/sphinx/extensions/lobster/BUILD b/tools/sphinx/extensions/lobster/BUILD new file mode 100644 index 00000000..2acc5c02 --- /dev/null +++ b/tools/sphinx/extensions/lobster/BUILD @@ -0,0 +1,21 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +py_library( + name = "lobster", + srcs = ["lobster.py"], + imports = ["."], + visibility = [ + "//bazel/rules/rules_score:__pkg__", + "//tools/sphinx:__pkg__", + ], +) diff --git a/tools/sphinx/extensions/lobster/README.md b/tools/sphinx/extensions/lobster/README.md new file mode 100644 index 00000000..3dbf064d --- /dev/null +++ b/tools/sphinx/extensions/lobster/README.md @@ -0,0 +1,47 @@ +# Sphinx Lobster Extension + +Integrates LOBSTER traceability into Sphinx documentation builds. + +## Usage + +The extension registers a custom Sphinx builder named `lobster`. It is used +automatically by the `rules_score` Sphinx build integration. + +The builder scans RST documents for `:requirement:upstream-ref:` roles and +produces a `_merged.lobster` file that feeds into the LOBSTER traceability chain. + +## Flow + +``` +Sphinx build (builder=lobster) + │ + ▼ +LobsterBuilder (subclass of TextBuilder) + - LobsterTranslator parses :requirement:upstream-ref: roles + - Each .rst file → /.json (lobster-imp-trace fragment) + │ + ▼ +LobsterBuilder.finish() + - Merges all per-doc JSON fragments into /_merged.lobster + │ + ▼ +OutputGroupInfo(lobster = []) + (returned by the sphinx Bazel rule) + │ + ▼ +sphinx_lobster_merge rule (tools/lobster/sphinx_lobster.bzl) + - pure provider adapter + │ + ▼ +LobsterProvider(lobster_input = {"sphinx_docs.lobster": "/_merged.lobster"}) +``` + +## Architecture + +The extension consists of two classes in `lobster.py`: + +- **`LobsterTranslator`** (`SphinxTranslator` subclass) — visits the RST document tree, + collects `upstream-ref` and `downstream-ref` role references, and builds a lobster + JSON structure per document. +- **`LobsterBuilder`** (`TextBuilder` subclass) — Sphinx builder that writes one `.json` + file per document, then merges all fragments into `_merged.lobster` in its `finish()` hook. diff --git a/tools/sphinx/extensions/lobster/lobster.py b/tools/sphinx/extensions/lobster/lobster.py new file mode 100644 index 00000000..6fbd25e4 --- /dev/null +++ b/tools/sphinx/extensions/lobster/lobster.py @@ -0,0 +1,202 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +from __future__ import annotations + +from os import path +from pathlib import Path +from typing import TYPE_CHECKING + +from sphinx.builders.text import TextBuilder +from sphinx.locale import __ +from sphinx.util.docutils import SphinxTranslator +from docutils import nodes + +import json + + +class LobsterTranslator(SphinxTranslator): + builder: LobsterBuilder + + def __init__(self, document: nodes.document, builder: LobsterBuilder) -> None: + super().__init__(document, builder) + + self.source = "" + self.docname = "" + self.line = "" + self.section_path = [] + self.tags = {} + + self.lobster_json = {} + + self.lobster_json["data"] = [] + + self.lobster_json["generator"] = "lobster_sphinx" + self.lobster_json["schema"] = "lobster-imp-trace" + self.lobster_json["version"] = 3 + + def _current_tag_name(self) -> str: + section_str = ".".join(map(str, self.section_path)) + return self.docname + "." + section_str if self.docname else section_str + + def _ensure_tag(self, tag_name: str) -> None: + """Create a lobster item for tag_name if it does not exist yet.""" + if tag_name not in self.tags: + self.tags[tag_name] = {} + self.tags[tag_name]["tag"] = "sphinx " + tag_name + + loc = {} + loc["kind"] = "file" + loc["file"] = self.source + loc["line"] = ( + self.line if (isinstance(self.line, int) and self.line >= 1) else None + ) + loc["column"] = None + + self.tags[tag_name]["location"] = loc + + self.tags[tag_name]["name"] = tag_name + self.tags[tag_name]["messages"] = [] + self.tags[tag_name]["just_up"] = [] + self.tags[tag_name]["just_down"] = [] + self.tags[tag_name]["just_global"] = [] + self.tags[tag_name]["language"] = "rst" + self.tags[tag_name]["kind"] = "section" + self.tags[tag_name]["refs"] = [] + + self.lobster_json["data"].append(self.tags[tag_name]) + + def add_ref(self, name: str) -> None: + """Record an upstream tracing reference (upstream-ref role).""" + tag_name = self._current_tag_name() + self._ensure_tag(tag_name) + if name not in self.tags[tag_name]["refs"]: + self.tags[tag_name]["refs"].append(name) + + def add_downstream_ref(self, name: str) -> None: + """Record a downstream reference (downstream-ref role). + + Downstream refs are stored in just_down so lobster does not try to + resolve them as upstream tracing targets. + """ + tag_name = self._current_tag_name() + self._ensure_tag(tag_name) + if name not in self.tags[tag_name]["just_down"]: + self.tags[tag_name]["just_down"].append(name) + + def visit_document(self, node: Element) -> None: + self.source = node.attributes["source"] + # Derive a short doc identifier from the source path. + # Bazel sandboxes produce paths like .../.../_sources/sub/doc.rst + source = self.source + if "_sources/" in source: + doc_rel = source.split("_sources/", 1)[1] + else: + doc_rel = Path(source).name + if doc_rel.endswith(".rst"): + doc_rel = doc_rel[:-4] + # Convert slashes to dots and remove spaces for a clean identifier. + self.docname = doc_rel.replace("/", ".").replace(" ", "") + + def depart_document(self, node: Element) -> None: + self.body = json.dumps(self.lobster_json, indent=4) + + def visit_comment(self, node: Element) -> None: # type: ignore[override] + raise nodes.SkipNode + + def visit_toctree(self, node: Element) -> None: + raise nodes.SkipNode + + def visit_index(self, node: Element) -> None: + raise nodes.SkipNode + + def visit_tabular_col_spec(self, node: Element) -> None: + raise nodes.SkipNode + + def visit_pending_xref(self, node: Element) -> None: + # pending_xref nodes are normally resolved before write_doc runs, + # but handle them here as a fallback. + if node.get("refdomain") == "requirement": + if node.get("reftype") == "upstream-ref": + self.add_ref("req " + node.get("reftarget", "")) + elif node.get("reftype") == "downstream-ref": + self.add_downstream_ref("req " + node.get("reftarget", "")) + raise nodes.SkipNode + + def visit_literal(self, node: Element) -> None: + # Resolved xref roles become literal nodes whose classes carry the + # role name: 'requirement-upstream-ref' or 'requirement-downstream-ref'. + # Prefix with "req " to match the lobster-trlc requirement tag format. + classes = node.get("classes", []) + ref = "req " + node.astext() + if "requirement-upstream-ref" in classes: + self.add_ref(ref) + elif "requirement-downstream-ref" in classes: + self.add_downstream_ref(ref) + + def visit_paragraph(self, node: Element) -> None: + self.line = node.line + + def visit_section(self, node: Element) -> None: + names = node.attributes.get("names", []) + self.section_path.append(names[0].replace(" ", "") if names else "") + + def depart_section(self, node: Element) -> None: + self.section_path.pop() + + def unknown_visit(self, node: nodes.Node) -> None: + pass + + def unknown_departure(self, node: nodes.Node) -> None: + pass + + +class LobsterBuilder(TextBuilder): + name = "lobster" + format = "json" + epilog = __("The text files are in %(outdir)s.") + + out_suffix = ".json" + allow_parallel = True + default_translator_class = LobsterTranslator + + def finish(self) -> None: + super().finish() + # Merge all per-document .json files written by write_doc() into a + # single lobster implementation-trace file so consumers don't need a + # separate merge step. + merged: dict = { + "data": [], + "generator": "lobster_sphinx", + "schema": "lobster-imp-trace", + "version": 3, + } + for json_file in sorted(Path(self.outdir).rglob("*.json")): + with open(json_file, encoding="utf-8") as fh: + doc = json.load(fh) + merged["data"].extend(doc.get("data", [])) + # NOTE: The output filename "_merged.lobster" is referenced by the + # Bazel rule in tools/lobster/lobster_sphinx.bzl (_lobster_sphinx_impl). + # Both names must stay in sync. + merged_path = path.join(self.outdir, "_merged.lobster") + with open(merged_path, "w", encoding="utf-8") as fh: + json.dump(merged, fh, indent=4) + + +def setup(app: Sphinx): + app.add_builder(LobsterBuilder) + + return { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } From 49e6a94da16c16cb6db3a59e21a763170c16b348 Mon Sep 17 00:00:00 2001 From: Ulrich Huber Date: Tue, 14 Apr 2026 10:11:37 +0200 Subject: [PATCH 02/10] Bump rules_score --- MODULE.bazel | 19 + bazel/rules/rules_score/BUILD | 150 +++-- bazel/rules/rules_score/README.md | 196 ++++++ bazel/rules/rules_score/private/BUILD | 18 + .../private/architectural_design.bzl | 166 ++++- .../private/architecture_aspect.bzl | 67 ++ .../private/assumptions_of_use.bzl | 57 +- .../private/cc_dependency_aspect.bzl | 40 ++ bazel/rules/rules_score/private/component.bzl | 213 ++++++- .../private/dependability_analysis.bzl | 267 +++++--- .../private/dependable_element.bzl | 581 ++++++++++++++++-- bazel/rules/rules_score/private/fmea.bzl | 380 ++++++++++-- .../rules_score/private/lobster_config.bzl | 33 + .../rules_score/private/requirements.bzl | 171 ++++++ .../rules_score/private/sphinx_module.bzl | 35 +- bazel/rules/rules_score/private/unit.bzl | 64 +- .../rules/rules_score/private/unit_design.bzl | 119 +++- bazel/rules/rules_score/providers.bzl | 173 ++++-- bazel/rules/rules_score/rules_score.bzl | 26 +- bazel/rules/rules_score/sphinx_toolchain.bzl | 19 +- .../src/arch_to_reqs_from_lobster.py | 146 +++++ .../rules_score/src/safety_analysis_tools.py | 279 +++++++++ .../rules_score/src/sphinx_html_merge.py | 12 - .../rules_score/src/sphinx_module_ext.py | 136 ++++ .../rules_score/templates/conf.template.py | 106 +++- ... => dependable_element_index.template.rst} | 30 +- .../rules_score/templates/fmea.template.rst | 8 + bazel/rules/rules_score/test/BUILD | 189 +++--- .../rules_score/test/check_unknown_needs.sh | 0 .../rules_score/test/fixtures/mock_test.sh | 0 .../fixtures/seooc_test/asr_fixtures.trlc | 22 + .../seooc_test/comp_req_fixtures.trlc | 22 + .../seooc_test/feat_req_fixtures.trlc | 22 + bazel/rules/rules_score/test/requirements.in | 8 - bazel/rules/rules_score/test/requirements.txt | 509 --------------- bazel/rules/rules_score/test/test/BUILD | 455 ++++++++++++++ .../test/test/fixtures/mock_lib1.cc | 4 + .../test/test/fixtures/mock_lib2.cc | 4 + .../test/test/fixtures/mock_test.sh | 18 + .../test/test/fixtures/module_a/index.rst | 31 + .../test/test/fixtures/module_b/index.rst | 37 ++ .../test/test/fixtures/module_c/index.rst | 29 + .../seooc_test/architectural_design.rst | 174 ++++++ .../fixtures/seooc_test/asr_fixtures.trlc | 22 + .../seooc_test/assumptions_of_use.rst | 80 +++ .../seooc_test/comp_req_fixtures.trlc | 22 + .../seooc_test/component_requirements.rst | 105 ++++ .../seooc_test/dependability_analysis.rst | 292 +++++++++ .../test/test/fixtures/seooc_test/dfa.rst | 149 +++++ .../seooc_test/dynamic_architecture.rst | 66 ++ .../seooc_test/feat_req_fixtures.trlc | 22 + .../seooc_test/feature_requirements.rst | 48 ++ .../seooc_test/static_architecture.rst | 45 ++ .../test/fixtures/test_binary_unit_test.cc | 16 + .../test/test/fixtures/test_component_main.cc | 13 + .../test/test/fixtures/test_unit2_test.cc | 16 + .../test/test/fixtures/test_unit_test.cc | 16 + .../test/test/html_generation_test.bzl | 223 +++++++ .../test/test/score_module_providers_test.bzl | 323 ++++++++++ .../rules_score/test/test/seooc_test.bzl | 135 ++++ .../test/test/test_safety_analysis_tools.py | 359 +++++++++++ .../test/test/unit_component_test.bzl | 181 ++++++ 62 files changed, 6044 insertions(+), 1124 deletions(-) create mode 100644 bazel/rules/rules_score/README.md create mode 100644 bazel/rules/rules_score/private/architecture_aspect.bzl create mode 100644 bazel/rules/rules_score/private/cc_dependency_aspect.bzl create mode 100644 bazel/rules/rules_score/private/lobster_config.bzl create mode 100644 bazel/rules/rules_score/private/requirements.bzl create mode 100644 bazel/rules/rules_score/src/arch_to_reqs_from_lobster.py create mode 100644 bazel/rules/rules_score/src/safety_analysis_tools.py create mode 100644 bazel/rules/rules_score/src/sphinx_module_ext.py rename bazel/rules/rules_score/templates/{seooc_index.template.rst => dependable_element_index.template.rst} (63%) create mode 100644 bazel/rules/rules_score/templates/fmea.template.rst mode change 100755 => 100644 bazel/rules/rules_score/test/check_unknown_needs.sh mode change 100755 => 100644 bazel/rules/rules_score/test/fixtures/mock_test.sh create mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/asr_fixtures.trlc create mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/comp_req_fixtures.trlc create mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/feat_req_fixtures.trlc delete mode 100644 bazel/rules/rules_score/test/requirements.in delete mode 100644 bazel/rules/rules_score/test/requirements.txt create mode 100644 bazel/rules/rules_score/test/test/BUILD create mode 100644 bazel/rules/rules_score/test/test/fixtures/mock_lib1.cc create mode 100644 bazel/rules/rules_score/test/test/fixtures/mock_lib2.cc create mode 100755 bazel/rules/rules_score/test/test/fixtures/mock_test.sh create mode 100644 bazel/rules/rules_score/test/test/fixtures/module_a/index.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/module_b/index.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/module_c/index.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/architectural_design.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/asr_fixtures.trlc create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/assumptions_of_use.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/comp_req_fixtures.trlc create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/component_requirements.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/dependability_analysis.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/dfa.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/dynamic_architecture.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/feat_req_fixtures.trlc create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/feature_requirements.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/static_architecture.rst create mode 100644 bazel/rules/rules_score/test/test/fixtures/test_binary_unit_test.cc create mode 100644 bazel/rules/rules_score/test/test/fixtures/test_component_main.cc create mode 100644 bazel/rules/rules_score/test/test/fixtures/test_unit2_test.cc create mode 100644 bazel/rules/rules_score/test/test/fixtures/test_unit_test.cc create mode 100644 bazel/rules/rules_score/test/test/html_generation_test.bzl create mode 100644 bazel/rules/rules_score/test/test/score_module_providers_test.bzl create mode 100644 bazel/rules/rules_score/test/test/seooc_test.bzl create mode 100644 bazel/rules/rules_score/test/test/test_safety_analysis_tools.py create mode 100644 bazel/rules/rules_score/test/test/unit_component_test.bzl diff --git a/MODULE.bazel b/MODULE.bazel index d7818c37..be9a983f 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -215,6 +215,25 @@ register_toolchains( ############################################################################### bazel_dep(name = "score_docs_as_code", version = "3.0.1", dev_dependency = True) +############################################################################### +# Setup Googletest +############################################################################### +bazel_dep(name = "googletest", version = "1.17.0.bcr.2", dev_dependency = True) + +############################################################################### +# Blueprint Maven +############################################################################### +bazel_dep(name = "rules_jvm_external", version = "6.10") +maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") +maven.install( + name = "blueprint_maven_dependencies", + artifacts = [ + "net.sourceforge.plantuml:plantuml:1.2025.9", + ], + repositories = ["https://repo1.maven.org/maven2"], +) +use_repo(maven, "blueprint_maven_dependencies") + ############################################################################### # Dependencies for Manual Analysis ############################################################################### diff --git a/bazel/rules/rules_score/BUILD b/bazel/rules/rules_score/BUILD index ebcff223..ed463fcc 100644 --- a/bazel/rules/rules_score/BUILD +++ b/bazel/rules/rules_score/BUILD @@ -18,14 +18,14 @@ load( "sphinx_module", ) load("//bazel/rules/rules_score:sphinx_toolchain.bzl", "sphinx_toolchain") +load("//lobster_bazel:lobster_bazel.bzl", "lobster_linker") exports_files([ "templates/conf.template.py", - "templates/seooc_index.template.rst", + "templates/dependable_element_index.template.rst", "templates/unit.template.rst", "templates/component.template.rst", - "src/sphinx_wrapper.py", - "src/bazel_sphinx_needs.py", + "templates/fmea.template.rst", ]) compile_pip_requirements( @@ -37,20 +37,22 @@ compile_pip_requirements( ], ) -sphinx_module( - name = "rules_score_doc", - srcs = glob( - [ - "docs/**/*.rst", - "docs/**/*.puml", - ], - allow_empty = True, - ), - index = "docs/index.rst", +# Arch-to-reqs-from-lobster tool: extracts requirements from component requirement .lobster files +# and generates an architecture.lobster item representing the component's allocation +py_binary( + name = "arch_to_reqs_from_lobster", + srcs = ["src/arch_to_reqs_from_lobster.py"], + main = "src/arch_to_reqs_from_lobster.py", + visibility = ["//visibility:public"], +) + +# Safety analysis tools (preprocess FTA PlantUML + extract lobster traceability items) +py_binary( + name = "safety_analysis_tools", + srcs = ["src/safety_analysis_tools.py"], + imports = ["src"], + main = "src/safety_analysis_tools.py", visibility = ["//visibility:public"], - deps = [ - # "@score_process//:score_process_module", - ], ) # HTML merge tool @@ -63,50 +65,118 @@ py_binary( py_library( name = "sphinx_module_ext", - srcs = ["src/bazel_sphinx_needs.py"], + srcs = ["src/sphinx_module_ext.py"], imports = ["src"], - visibility = ["//visibility:public"], + deps = [ + requirement("sphinx"), + ], ) # Sphinx build binary with all required dependencies py_binary( - name = "sphinx_build", + name = "score_build", srcs = ["src/sphinx_wrapper.py"], + data = [ + "//tools/sphinx:plantuml", + ], + env = { + "SOURCE_DIRECTORY": "", + "DATA": "", + "ACTION": "check", + }, main = "src/sphinx_wrapper.py", visibility = ["//visibility:public"], deps = [ ":sphinx_module_ext", - "@pip_tooling//myst_parser", - "@pip_tooling//sphinx", - "@pip_tooling//sphinx_design", - "@pip_tooling//sphinx_needs", - "@pip_tooling//sphinx_rtd_theme", - "@pip_tooling//sphinxcontrib_plantuml", + "//tools/sphinx/extensions/lobster", + "@score_docs_as_code//src/extensions/score_metamodel", + "@score_tooling//plantuml/sphinx/clickable_plantuml", + "@trlc//tools/sphinx/extensions/trlc", + requirement("sphinx"), + requirement("sphinx_rtd_theme"), + requirement("sphinx_needs"), + requirement("sphinx_design"), + requirement("myst_parser"), + requirement("readthedocs_sphinx_ext"), + requirement("rst2pdf"), + requirement("sphinxcontrib-umlet"), + requirement("svglib"), + requirement("sphinxcontrib-plantuml"), + "@rules_python//python/runfiles", ], ) -sphinx_toolchain( - name = "default_toolchain", - conf_template = "@score_tooling//bazel/rules/rules_score:templates/conf.template.py", - html_merge_tool = ":sphinx_html_merge", - sphinx = ":sphinx_build", +sphinx_module( + name = "rules_score_doc", + srcs = glob( + [ + "docs/**/*.rst", + "docs/**/*.puml", + ], + allow_empty = True, + ), + index = "docs/index.rst", + visibility = ["//visibility:public"], ) -toolchain_type( - name = "toolchain_type", +# HTML merge tool + +py_binary( + name = "raw_build", + srcs = ["src/sphinx_wrapper.py"], + env = { + "SOURCE_DIRECTORY": "", + "DATA": "", + "ACTION": "check", + }, + main = "src/sphinx_wrapper.py", visibility = ["//visibility:public"], + deps = [ + "//tools/sphinx/extensions/lobster", + "@score_docs_as_code//src/extensions/score_metamodel", + "@score_tooling//plantuml/sphinx/clickable_plantuml", + "@trlc//tools/sphinx/extensions/trlc", + requirement("sphinx"), + requirement("sphinx_rtd_theme"), + requirement("sphinx_needs"), + requirement("sphinx_design"), + requirement("myst_parser"), + requirement("readthedocs_sphinx_ext"), + requirement("rst2pdf"), + requirement("sphinxcontrib-umlet"), + requirement("svglib"), + requirement("sphinxcontrib-plantuml"), + ], ) +sphinx_toolchain( + name = "default_toolchain", + sphinx = ":score_build", +) + +toolchain_type(name = "toolchain_type") + toolchain( name = "sphinx_default_toolchain", - exec_compatible_with = [ - "@platforms//os:linux", - "@platforms//cpu:x86_64", - ], - target_compatible_with = [ - "@platforms//os:linux", - "@platforms//cpu:x86_64", - ], toolchain = ":default_toolchain", toolchain_type = ":toolchain_type", ) + +# --------------------------------------------------------------------------- +# Lobster traceability – rules_score Bazel implementation +# --------------------------------------------------------------------------- + +filegroup( + name = "bzl_srcs", + srcs = glob(["*.bzl"]), +) + +lobster_linker( + name = "rules_score_impl", + srcs = [ + ":bzl_srcs", + "//bazel/rules/rules_score/private:bzl_srcs", + "//bazel/rules/rules_score/test:bzl_srcs", + ], + visibility = ["//docs/processes:__pkg__"], +) diff --git a/bazel/rules/rules_score/README.md b/bazel/rules/rules_score/README.md new file mode 100644 index 00000000..490245e9 --- /dev/null +++ b/bazel/rules/rules_score/README.md @@ -0,0 +1,196 @@ +# Rules Score + +Starlark rules implementing the **S-CORE** functional-safety development process +for safety related automotive software. + +## Rules Overview + +![Rules integration overview](docs/rules_score_overview.svg) + +| Rule | Providers emitted | +|------|-------------------| +| `feature_requirements` | `FeatureRequirementsInfo` | +| `component_requirements` | `ComponentRequirementsInfo` | +| `assumptions_of_use` | `AssumptionsOfUseInfo` | +| `architectural_design` | `ArchitecturalDesignInfo` | +| `unit` | `UnitInfo`, `CertifiedScope` | +| `component` | `ComponentInfo` | +| `fmea` | `AnalysisInfo` | +| `dependability_analysis` | `DependabilityAnalysisInfo` | +| `dependable_element` | HTML documentation zip (Sphinx) | + +All rules also emit `SphinxSourcesInfo` for the documentation assembly pipeline. + +--- + +## `feature_requirements` / `component_requirements` + +```starlark +load("@trlc//:trlc.bzl", "trlc_requirements") +load("//bazel/rules/rules_score:rules_score.bzl", "feature_requirements") + +trlc_requirements( + name = "my_trlc_reqs", + srcs = ["requirements.trlc"], + spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], +) + +feature_requirements( + name = "my_feat_reqs", + srcs = [":my_trlc_reqs"], +) +``` + +**`bazel build`** — collects `TrlcProviderInfo` from the underlying +`trlc_requirements` targets and produces `.lobster` files for LOBSTER. +Also generates a `_test` target that validates metamodel compliance. + +--- + +## `assumptions_of_use` + +```starlark +assumptions_of_use( + name = "my_aou", + requirements = [":my_feat_reqs"], + srcs = ["assumptions.rst"], +) +``` + +**`bazel build`** — collects LOBSTER traceability files from the linked +requirements and exposes them via `AssumptionsOfUseInfo`. + +--- + +## `architectural_design` + +```starlark +architectural_design( + name = "my_design", + static = ["class.puml", "component.puml"], + dynamic = ["sequence.puml"], + public_api = ["api.puml"], +) +``` + +**`bazel build`** — runs `puml_parser` on every `.puml` file, producing: +- a `.fbs.bin` FlatBuffers binary (diagram AST) — consumed by archver validation +- a `.lobster` traceability file (Interface elements only) — consumed by LOBSTER +- a `plantuml_links.json` — consumed by the `clickable_plantuml` Sphinx extension + +Diagrams in `public_api` are classified separately so their lobster items flow +through `public_api_lobster_files` for failure-mode traceability. + +--- + +## `unit` + +```starlark +unit( + name = "my_unit", + unit_design = [":my_design"], + implementation = [":my_lib"], + tests = [":my_tests"], +) +``` + +**`bazel build`** — wraps implementation targets and collects design + test +references into `UnitInfo`. Also runs unit tests via `gtest_report` and +produces `.lobster` traceability items for LOBSTER. + +**`bazel test`** — executes the wrapped test targets. + +--- + +## `component` + +```starlark +component( + name = "my_component", + requirements = [":my_comp_reqs"], + components = [":unit_a", ":unit_b"], + tests = [], +) +``` + +**`bazel build`** — aggregates `UnitInfo` / nested `ComponentInfo` providers +and collects requirement + architecture + test lobster sources. + +**`bazel test`** — runs component-level integration tests passed via `tests`. + +--- + +## `fmea` + +```starlark +fmea( + name = "my_fmea", + failuremodes = [":failure_modes"], + controlmeasures = [":control_measures"], + root_causes = ["fta.puml"], + arch_design = ":my_design", +) +``` + +**`bazel build`** — generates `fmea.rst` (merged FM / CM / FTA sections), +runs `lobster-trlc` on TRLC inputs, and extracts FTA events from `.puml` +diagrams into `fta.lobster`. Build-only; traceability validation is done +by the wrapping `dependability_analysis` test. + +--- + +## `dependability_analysis` + +```starlark +dependability_analysis( + name = "my_da", + fmea = [":my_fmea"], + arch_design = ":my_design", +) +``` + +**`bazel build`** — collects `.lobster` files from all sub-analyses and +architectural design, expands the `lobster_sa.conf` template, and runs +`lobster-ci-report` to produce a traceability report JSON + HTML. + +**`bazel test`** — asserts that `lobster-ci-report` exits with code 0 +(all traceability links are satisfied). This is the primary +**safety-analysis traceability gate**. + +```bash +bazel test //examples/seooc:sample_dependability_analysis +``` + +--- + +## `dependable_element` + +```starlark +dependable_element( + name = "my_seooc", + description = "My safety element", + integrity_level = "B", + requirements = [":feat_reqs"], + architectural_design = [":my_design"], + dependability_analysis = [":my_da"], + components = [":my_component"], + assumptions_of_use = [], + tests = [], +) +``` + +**`bazel build`** — generates a complete HTML documentation zip via Sphinx. +Internally: +1. `_dependable_element_index` generates an `index.rst` aggregating all + artifacts, runs archver architecture validation as a subrule, and + produces a DE-level LOBSTER report (`lobster_de.conf` template covering + Feature Req → Component Req → Architecture → Public API → Failure Modes). +2. `sphinx_module` compiles all RST sources + diagrams into an HTML zip. + +**`bazel test`** — runs the LOBSTER CI report embedded in the index rule +and all component / unit tests transitively. + +```bash +bazel build //examples/seooc:safety_software_seooc_example # HTML zip +bazel test //examples/seooc:safety_software_seooc_example # all tests +``` diff --git a/bazel/rules/rules_score/private/BUILD b/bazel/rules/rules_score/private/BUILD index e69de29b..6280e152 100644 --- a/bazel/rules/rules_score/private/BUILD +++ b/bazel/rules/rules_score/private/BUILD @@ -0,0 +1,18 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +filegroup( + name = "bzl_srcs", + srcs = glob(["*.bzl"]), + visibility = ["//bazel/rules/rules_score:__pkg__"], +) diff --git a/bazel/rules/rules_score/private/architectural_design.bzl b/bazel/rules/rules_score/private/architectural_design.bzl index 7ce0b548..1bcc02c4 100644 --- a/bazel/rules/rules_score/private/architectural_design.bzl +++ b/bazel/rules/rules_score/private/architectural_design.bzl @@ -17,46 +17,148 @@ Architectural Design build rules for S-CORE projects. This module provides macros and rules for defining architectural design documentation following S-CORE process guidelines. Architectural design documents describe the software architecture including static and dynamic views. + +The rule automatically invokes the PlantUML parser on .puml/.plantuml files +to produce FlatBuffers binary representations of the parsed diagrams. """ load("//bazel/rules/rules_score:providers.bzl", "ArchitecturalDesignInfo", "SphinxSourcesInfo") -# ArchitecturalDesignInfo is re-exported from providers.bzl for backward compatibility. - # ============================================================================ # Private Rule Implementation # ============================================================================ +def _run_puml_parser(ctx, puml_file): + """Run the PlantUML parser on a single .puml file to produce a FlatBuffers binary + and a lobster traceability file. + + The diagram type is auto-detected by the parser and encoded in the + FlatBuffers schema (each diagram type uses its own root_type). + Lobster output is produced in-process for component diagrams. + + Args: + ctx: Rule context + puml_file: The .puml File object to parse + + Returns: + Tuple of (fbs_output, lobster_output) declared output Files. + """ + file_stem = puml_file.basename.rsplit(".", 1)[0] + fbs_output = ctx.actions.declare_file( + "{}/{}.fbs.bin".format(ctx.label.name, file_stem), + ) + lobster_output = ctx.actions.declare_file( + "{}/{}.lobster".format(ctx.label.name, file_stem), + ) + + ctx.actions.run( + inputs = [puml_file], + outputs = [fbs_output, lobster_output], + executable = ctx.executable._puml_parser, + arguments = [ + "--file", + puml_file.path, + "--fbs-output-dir", + fbs_output.dirname, + "--lobster-output-dir", + lobster_output.dirname, + ], + progress_message = "Parsing PlantUML diagram: %s" % puml_file.short_path, + ) + + return fbs_output, lobster_output + +def _parse_puml_diagrams(ctx, files): + """Run the PlantUML parser on all .puml/.plantuml files in a list. + + Args: + ctx: Rule context + files: List of File objects + + Returns: + Tuple of (fbs_outputs, lobster_outputs) lists of generated Files. + """ + fbs_outputs = [] + lobster_outputs = [] + for f in files: + if f.extension in ("puml", "plantuml"): + fbs, lobster = _run_puml_parser(ctx, f) + fbs_outputs.append(fbs) + lobster_outputs.append(lobster) + return fbs_outputs, lobster_outputs + def _architectural_design_impl(ctx): """Implementation for architectural_design rule. Collects architectural design artifacts including static and dynamic - diagrams and provides them through the ArchitecturalDesignInfo provider. + diagrams, runs the PlantUML parser on .puml files to generate FlatBuffers + binaries, and provides them through the ArchitecturalDesignInfo provider. + + The diagram type (component, class, sequence) is auto-detected by the + parser and encoded in the FlatBuffers binary via its schema root_type. Args: ctx: Rule context Returns: - List of providers including DefaultInfo and ArchitecturalDesignInfo + List of providers including DefaultInfo, ArchitecturalDesignInfo, SphinxSourcesInfo """ - static_files = depset(ctx.files.static) - dynamic_files = depset(ctx.files.dynamic) - # Combine all files for DefaultInfo - all_files = depset( - transitive = [static_files, dynamic_files], + # Parse static and dynamic diagrams separately so each provider field + # carries the flatbuffers for its own category + static_fbs_list, static_lobster_list = _parse_puml_diagrams(ctx, ctx.files.static) + dynamic_fbs_list, dynamic_lobster_list = _parse_puml_diagrams(ctx, ctx.files.dynamic) + public_api_fbs_list, public_api_lobster_list = _parse_puml_diagrams(ctx, ctx.files.public_api) + + static_fbs = depset(static_fbs_list) + dynamic_fbs = depset(dynamic_fbs_list) + public_api_fbs = depset(public_api_fbs_list) + all_lobster = depset(static_lobster_list + dynamic_lobster_list + public_api_lobster_list) + public_api_lobster = depset(public_api_lobster_list) + + # Source files for SphinxSourcesInfo (sphinx documentation pipeline) + all_source_files = depset( + transitive = [depset(ctx.files.static), depset(ctx.files.dynamic), depset(ctx.files.public_api)], + ) + + # Run the linker on all generated .fbs.bin files to produce a + # plantuml_links.json for the clickable_plantuml Sphinx extension. + all_fbs_files = static_fbs.to_list() + dynamic_fbs.to_list() + public_api_fbs.to_list() + plantuml_links_json = ctx.actions.declare_file( + "{}/plantuml_links.json".format(ctx.label.name), + ) + if all_fbs_files: + ctx.actions.run( + inputs = all_fbs_files, + outputs = [plantuml_links_json], + executable = ctx.executable._linker, + arguments = ["--fbs-files"] + [f.path for f in all_fbs_files] + ["--output", plantuml_links_json.path], + progress_message = "Generating PlantUML links JSON for %s" % ctx.label.name, + ) + else: + ctx.actions.write( + output = plantuml_links_json, + content = '{"links":[]}', + ) + + sphinx_files = depset( + [plantuml_links_json], + transitive = [all_source_files], ) return [ - DefaultInfo(files = all_files), + DefaultInfo(files = all_source_files), ArchitecturalDesignInfo( - static = static_files, - dynamic = dynamic_files, + static = static_fbs, + dynamic = dynamic_fbs, name = ctx.label.name, + lobster_files = all_lobster, + public_api_lobster_files = public_api_lobster, ), + # Source diagram files + plantuml_links.json for the sphinx documentation build SphinxSourcesInfo( - srcs = all_files, - transitive_srcs = all_files, + srcs = sphinx_files, + deps = sphinx_files, ), ] @@ -66,18 +168,39 @@ def _architectural_design_impl(ctx): _architectural_design = rule( implementation = _architectural_design_impl, - doc = "Collects architectural design documents and diagrams for S-CORE process compliance", + doc = "Collects architectural design documents and diagrams for S-CORE process compliance. " + + "Automatically parses PlantUML files to produce FlatBuffers binary representations.", attrs = { "static": attr.label_list( - allow_files = [".puml", ".plantuml", ".png", ".svg", ".rst", ".md"], + allow_files = [".puml", ".plantuml", ".svg", ".rst", ".md"], mandatory = False, doc = "Static architecture diagrams (class diagrams, component diagrams, etc.)", ), "dynamic": attr.label_list( - allow_files = [".puml", ".plantuml", ".png", ".svg", ".rst", ".md"], + allow_files = [".puml", ".plantuml", ".svg", ".rst", ".md"], mandatory = False, doc = "Dynamic architecture diagrams (sequence diagrams, activity diagrams, etc.)", ), + "public_api": attr.label_list( + allow_files = [".puml", ".plantuml"], + mandatory = False, + doc = "Public API diagrams (parsed identically to static/dynamic). " + + "Classified separately so their lobster items are exposed via " + + "public_api_lobster_files, enabling failure-mode-to-interface " + + "traceability at the dependable element level.", + ), + "_puml_parser": attr.label( + default = Label("@score_tooling//plantuml/parser:parser"), + executable = True, + cfg = "exec", + doc = "PlantUML parser tool that generates FlatBuffers from .puml files", + ), + "_linker": attr.label( + default = Label("@score_tooling//plantuml/parser:linker"), + executable = True, + cfg = "exec", + doc = "Tool that generates plantuml_links.json from FlatBuffers diagram outputs", + ), }, ) @@ -89,6 +212,7 @@ def architectural_design( name, static = [], dynamic = [], + public_api = [], visibility = None): """Define architectural design following S-CORE process guidelines. @@ -108,6 +232,11 @@ def architectural_design( .png, .svg) or documentation files (.rst, .md) containing dynamic architecture views such as sequence diagrams, activity diagrams, or state diagrams as defined in the S-CORE process. + public_api: Optional list of .puml files describing the public interface + of this element. These are parsed identically to static/dynamic + diagrams but classified separately so their lobster items are + exposed via public_api_lobster_files, enabling failure-mode-to- + interface traceability at the dependable element level. visibility: Bazel visibility specification for the generated targets. Generated Targets: @@ -120,6 +249,7 @@ def architectural_design( static = [ "class_diagram.puml", "component_diagram.puml", + "component_overview.svg", ], dynamic = [ "sequence_diagram.puml", @@ -128,9 +258,11 @@ def architectural_design( ) ``` """ + _architectural_design( name = name, static = static, dynamic = dynamic, + public_api = public_api, visibility = visibility, ) diff --git a/bazel/rules/rules_score/private/architecture_aspect.bzl b/bazel/rules/rules_score/private/architecture_aspect.bzl new file mode 100644 index 00000000..62e01f7f --- /dev/null +++ b/bazel/rules/rules_score/private/architecture_aspect.bzl @@ -0,0 +1,67 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +""" +Architecture Aspect for collecting component and unit structure. + +This module provides an aspect that traverses the build graph to collect +architectural information about components and their contained units. +""" + +load("//bazel/rules/rules_score:providers.bzl", "ComponentInfo", "UnitInfo") + +CurrentArchitectureProviderInfo = provider( + doc = "Provider for collecting component and unit architecture from the build graph", + fields = { + "components": "Dictionary mapping component labels to their structure (units and nested components)", + }, +) + +def _collect_current_architecture_aspect_impl(target, ctx): + components_dict = {} + + # Process components attribute - this contains both units and nested components + if hasattr(ctx.rule.attr, "components"): + current_units = [] + current_components = [] + + for comp in ctx.rule.attr.components: + comp_label = str(comp.label) + + # Check if it's a unit or a component + if UnitInfo in comp: + current_units.append(comp_label) + elif ComponentInfo in comp: + current_components.append(comp_label) + + if CurrentArchitectureProviderInfo in comp: + nested_info = comp[CurrentArchitectureProviderInfo] + + # Merge all nested components into flat hierarchy + components_dict.update(nested_info.components) + + component_structure = {} + if current_units: + component_structure["units"] = current_units + if current_components: + component_structure["components"] = current_components + components_dict[str(target.label)] = component_structure + + return [CurrentArchitectureProviderInfo( + components = components_dict, + )] + +collect_current_architecture_aspect = aspect( + implementation = _collect_current_architecture_aspect_impl, + attr_aspects = ["components"], +) diff --git a/bazel/rules/rules_score/private/assumptions_of_use.bzl b/bazel/rules/rules_score/private/assumptions_of_use.bzl index 58ba7309..97f6c52b 100644 --- a/bazel/rules/rules_score/private/assumptions_of_use.bzl +++ b/bazel/rules/rules_score/private/assumptions_of_use.bzl @@ -21,8 +21,6 @@ operating conditions and constraints for a Safety Element out of Context (SEooC) load("//bazel/rules/rules_score:providers.bzl", "AssumptionsOfUseInfo", "ComponentRequirementsInfo", "FeatureRequirementsInfo", "SphinxSourcesInfo") -# AssumptionsOfUseInfo is re-exported from providers.bzl for backward compatibility. - # ============================================================================ # Private Rule Implementation # ============================================================================ @@ -41,28 +39,34 @@ def _assumptions_of_use_impl(ctx): """ srcs = depset(ctx.files.srcs) - # Collect feature requirements providers - feature_reqs = [] - for feat_req in ctx.attr.feature_requirements: - if FeatureRequirementsInfo in feat_req: - feature_reqs.append(feat_req[FeatureRequirementsInfo]) - - # Collect transitive sphinx sources from feature requirements + # Collect requirements providers and lobster files + reqs = [] + lobster_files = [] + for req in ctx.attr.requirements: + if FeatureRequirementsInfo in req: + info = req[FeatureRequirementsInfo] + reqs.append(info) + lobster_files.append(info.srcs) + elif ComponentRequirementsInfo in req: + info = req[ComponentRequirementsInfo] + reqs.append(info) + lobster_files.append(info.srcs) + + # Collect transitive sphinx sources from requirements transitive = [srcs] - for feat_req in ctx.attr.feature_requirements: - if SphinxSourcesInfo in feat_req: - transitive.append(feat_req[SphinxSourcesInfo].transitive_srcs) + for req in ctx.attr.requirements: + if SphinxSourcesInfo in req: + transitive.append(req[SphinxSourcesInfo].deps) return [ DefaultInfo(files = srcs), AssumptionsOfUseInfo( - srcs = srcs, - feature_requirements = feature_reqs, + srcs = depset(transitive = lobster_files), name = ctx.label.name, ), SphinxSourcesInfo( srcs = srcs, - transitive_srcs = depset(transitive = transitive), + deps = depset(transitive = transitive), ), ] @@ -79,15 +83,10 @@ _assumptions_of_use = rule( mandatory = True, doc = "Source files containing Assumptions of Use specifications", ), - "feature_requirements": attr.label_list( - providers = [FeatureRequirementsInfo], - mandatory = False, - doc = "List of feature_requirements targets that these Assumptions of Use trace to", - ), - "component_requirements": attr.label_list( - providers = [ComponentRequirementsInfo], + "requirements": attr.label_list( + providers = [[FeatureRequirementsInfo], [ComponentRequirementsInfo]], mandatory = False, - doc = "List of feature_requirements targets that these Assumptions of Use trace to", + doc = "List of feature or component requirements targets that these Assumptions of Use trace to", ), }, ) @@ -99,8 +98,7 @@ _assumptions_of_use = rule( def assumptions_of_use( name, srcs, - feature_requirement = [], - component_requirements = [], + requirements = [], visibility = None): """Define Assumptions of Use following S-CORE process guidelines. @@ -115,8 +113,8 @@ def assumptions_of_use( srcs: List of labels to .rst, .md, or .trlc files containing the Assumptions of Use specifications as defined in the S-CORE process. - feature_requirement: Optional list of labels to feature_requirements - targets that these Assumptions of Use relate to. Establishes + requirements: Optional list of labels to feature or component requirements + targets that these Assumptions of Use trace to. Establishes traceability as defined in the S-CORE process. visibility: Bazel visibility specification for the generated targets. @@ -128,14 +126,13 @@ def assumptions_of_use( assumptions_of_use( name = "my_assumptions_of_use", srcs = ["assumptions_of_use.rst"], - feature_requirement = [":my_feature_requirements"], + requirements = [":my_feature_requirements"], ) ``` """ _assumptions_of_use( name = name, srcs = srcs, - feature_requirements = feature_requirement, - component_requirements = component_requirements, + requirements = requirements, visibility = visibility, ) diff --git a/bazel/rules/rules_score/private/cc_dependency_aspect.bzl b/bazel/rules/rules_score/private/cc_dependency_aspect.bzl new file mode 100644 index 00000000..34e851c8 --- /dev/null +++ b/bazel/rules/rules_score/private/cc_dependency_aspect.bzl @@ -0,0 +1,40 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +""" +Aspect for collecting transitive dependencies from cc_library and cc_binary targets. + +This module provides an aspect that traverses the build graph of C/C++ targets +and collects all labels that they depend on transitively. +""" + +load("//bazel/rules/rules_score:providers.bzl", "CcDependencyInfo") + +def _cc_dependencies_aspect_impl(target, ctx): + collected_dependencies = depset([target.label]) + + for attr_name in ["deps", "implementation_deps", "exported_deps"]: + if hasattr(ctx.rule.attr, attr_name): + deps = getattr(ctx.rule.attr, attr_name) + for dep in deps: + if CcDependencyInfo in dep: + collected_dependencies = depset(transitive = [collected_dependencies, dep[CcDependencyInfo].dependencies]) + + return [CcDependencyInfo( + dependencies = collected_dependencies, + )] + +cc_dependencies_aspect = aspect( + implementation = _cc_dependencies_aspect_impl, + attr_aspects = ["deps", "implementation_deps", "exported_deps"], +) diff --git a/bazel/rules/rules_score/private/component.bzl b/bazel/rules/rules_score/private/component.bzl index 4e41b9e8..12de20bb 100644 --- a/bazel/rules/rules_score/private/component.bzl +++ b/bazel/rules/rules_score/private/component.bzl @@ -19,7 +19,9 @@ following S-CORE process guidelines. A component consists of multiple units with associated requirements and tests. """ -load("//bazel/rules/rules_score:providers.bzl", "ComponentInfo", "SphinxSourcesInfo") +load("@lobster//:lobster.bzl", "subrule_lobster_gtest", "subrule_lobster_html_report", "subrule_lobster_report") +load("//bazel/rules/rules_score:providers.bzl", "CertifiedScope", "ComponentInfo", "ComponentRequirementsInfo", "SphinxSourcesInfo", "UnitInfo") +load("//bazel/rules/rules_score/private:lobster_config.bzl", "format_lobster_sources") # ============================================================================ # Private Rule Implementation @@ -38,34 +40,164 @@ def _component_impl(ctx): List of providers including DefaultInfo and ComponentInfo """ - # Collect requirements files from component_requirements targets - requirements_files = [] + # ------------------------------------------------------------------------- + # Sphinx Docs: collect RST sources from component_requirements targets + # and bubble up SphinxSourcesInfo from sub-components/units + # ------------------------------------------------------------------------- + req_sphinx_files = [] for req_target in ctx.attr.requirements: if SphinxSourcesInfo in req_target: - requirements_files.append(req_target[SphinxSourcesInfo].srcs) + req_sphinx_files.append(req_target[SphinxSourcesInfo].srcs) - requirements_depset = depset(transitive = requirements_files) + component_sphinx_files = [] + for component in ctx.attr.components: + if SphinxSourcesInfo in component: + component_sphinx_files.append(component[SphinxSourcesInfo].deps) - # Collect components and tests + req_sphinx_depset = depset(transitive = req_sphinx_files) + sphinx_depset = depset(transitive = req_sphinx_files + component_sphinx_files) + + # ------------------------------------------------------------------------- + # Lobster Tracing: collect .lobster files from component_requirements targets + # ------------------------------------------------------------------------- + req_lobster_files = [] + for req_target in ctx.attr.requirements: + if ComponentRequirementsInfo in req_target: + req_lobster_files.append(req_target[ComponentRequirementsInfo].srcs) + + req_lobster_depset = depset(transitive = req_lobster_files) + + # Collect nested components components_depset = depset(ctx.attr.components) - tests_depset = depset(ctx.attr.tests) - # Combine all files for DefaultInfo - all_files = depset( - transitive = [requirements_depset], + # ------------------------------------------------------------------------- + # Collect Dependencies and Certification Scopes from sub-components/units + # ------------------------------------------------------------------------- + collected_certified_scopes = [] + collected_dependencies = [] + for component in ctx.attr.components: + if UnitInfo in component: + if component[UnitInfo].dependent_labels: + collected_dependencies.append(component[UnitInfo].dependent_labels) + if ComponentInfo in component: + if component[ComponentInfo].dependent_labels: + collected_dependencies.append(component[ComponentInfo].dependent_labels) + if CertifiedScope in component: + collected_certified_scopes.append(component[CertifiedScope].transitive_scopes) + + # ------------------------------------------------------------------------- + # Propagate Tracing Files: collect .lobster files from sub-components + # and unit test results from units (to be converted to gtest.lobster below) + # ------------------------------------------------------------------------- + collected_unit_test_files = [] + collected_tests = [] + collected_architecture = [] + for component in ctx.attr.components: + if UnitInfo in component: + collected_unit_test_files.append(component[UnitInfo].tests) + if ComponentInfo in component: + if component[ComponentInfo].tests: + collected_tests.append(component[ComponentInfo].tests) + if component[ComponentInfo].architecture: + collected_architecture.append(component[ComponentInfo].architecture) + + # Convert unit test XML files to a single gtest.lobster file + gtest_lobster_file, provider = subrule_lobster_gtest(depset(transitive = collected_unit_test_files).to_list()) + + # ------------------------------------------------------------------------- + # Architecture Lobster: generate architecture.lobster for this component + # Creates a single lobster item representing the component, referencing + # all component requirements allocated to it through Bazel. + # ------------------------------------------------------------------------- + arch_lobster_file = None + if req_lobster_files: + arch_lobster_file = ctx.actions.declare_file(ctx.attr.name + "_architecture.lobster") + arch_to_reqs_args = ctx.actions.args() + arch_to_reqs_args.add("--component-name", "//%s:%s" % (ctx.label.package, ctx.label.name)) + arch_to_reqs_args.add("--build-file", ctx.label.package + "/BUILD") + arch_to_reqs_args.add("--output", arch_lobster_file) + arch_to_reqs_args.add("--req-lobster") + arch_to_reqs_args.add_all(req_lobster_depset) + + ctx.actions.run( + inputs = req_lobster_depset.to_list(), + outputs = [arch_lobster_file], + executable = ctx.executable._arch_to_reqs_from_lobster, + arguments = [arch_to_reqs_args], + progress_message = "Generating architecture lobster for %s" % ctx.label, + ) + + # ------------------------------------------------------------------------- + # Generate Lobster Configuration: expand static template, substituting + # only the source file lists (the structure is fixed per variant). + # ------------------------------------------------------------------------- + comp_req_lobster_files = req_lobster_depset.to_list() + all_lobster_inputs = list(comp_req_lobster_files) + + if arch_lobster_file: + all_lobster_inputs.append(arch_lobster_file) + + all_lobster_inputs.append(gtest_lobster_file) + + lobster_config = ctx.actions.declare_file(ctx.attr.name + "_traceability_config") + ctx.actions.expand_template( + template = ctx.file._lobster_comp_template, + output = lobster_config, + substitutions = { + "{COMP_REQ_SOURCES}": format_lobster_sources(comp_req_lobster_files), + "{ARCH_SOURCES}": format_lobster_sources([arch_lobster_file] if arch_lobster_file else []), + "{UNIT_TEST_SOURCES}": format_lobster_sources([gtest_lobster_file]), + }, + ) + + # ------------------------------------------------------------------------- + # Lobster Report Build: produce .lobster report, HTML + # ------------------------------------------------------------------------- + lobster_report = subrule_lobster_report(all_lobster_inputs, lobster_config) + lobster_html_report = subrule_lobster_html_report(lobster_report) + + test_executable = ctx.actions.declare_file("{}_lobster_ci_test_executable".format(ctx.attr.name)) + ctx.actions.write( + output = test_executable, + content = "set -o pipefail; {} {}".format( + ctx.executable._lobster_ci_report.short_path, + lobster_report.short_path, + ), ) return [ - DefaultInfo(files = all_files), + # DefaultInfo: lobster report as build output + CI test executable + DefaultInfo( + runfiles = ctx.runfiles( + files = [ + ctx.executable._lobster_ci_report, + lobster_report, + ], + ).merge(ctx.attr._lobster_ci_report[DefaultInfo].default_runfiles), + files = depset([lobster_report, lobster_html_report]), + executable = test_executable, + ), + # CertifiedScope: propagate certification scopes upward + CertifiedScope(transitive_scopes = depset(transitive = collected_certified_scopes)), + # ComponentInfo: lobster traceability files for requirements, architecture, and tests; propagated up to dependable_element ComponentInfo( name = ctx.label.name, - requirements = requirements_depset, + requirements = req_lobster_depset, components = components_depset, - tests = tests_depset, + tests = depset( + [gtest_lobster_file], + transitive = collected_tests, + ), + architecture = depset( + [arch_lobster_file] if arch_lobster_file else [], + transitive = collected_architecture, + ), + dependent_labels = depset(transitive = collected_dependencies), ), + # SphinxSourcesInfo: RST sources from component requirements + transitive sources from sub-components/units SphinxSourcesInfo( - srcs = all_files, - transitive_srcs = all_files, + srcs = req_sphinx_depset, + deps = sphinx_depset, ), ] @@ -73,23 +205,42 @@ def _component_impl(ctx): # Rule Definition # ============================================================================ -_component = rule( +_component_test = rule( implementation = _component_impl, doc = "Defines a software component composed of multiple units for S-CORE process compliance", attrs = { "requirements": attr.label_list( mandatory = True, - doc = "Component requirements artifacts (typically component_requirements targets)", + providers = [ComponentRequirementsInfo], + doc = "Component requirements artifacts (component_requirements targets only)", ), "components": attr.label_list( - mandatory = True, - doc = "Unit targets that comprise this component", + providers = [[ComponentInfo], [UnitInfo]], + doc = "Nested component or unit targets (components can contain both components and units)", ), "tests": attr.label_list( mandatory = True, doc = "Component-level integration test targets", ), + "_lobster_ci_report": attr.label( + default = "@lobster//:lobster-ci-report", + executable = True, + cfg = "exec", + ), + "_arch_to_reqs_from_lobster": attr.label( + default = Label("//bazel/rules/rules_score:arch_to_reqs_from_lobster"), + executable = True, + cfg = "exec", + doc = "Tool to extract component requirements and generate architecture .lobster items for component traceability", + ), + "_lobster_comp_template": attr.label( + default = Label("//bazel/rules/rules_score/config:lobster_component_template"), + allow_single_file = True, + doc = "Lobster config template for component traceability.", + ), }, + subrules = [subrule_lobster_gtest, subrule_lobster_report, subrule_lobster_html_report], + test = True, ) # ============================================================================ @@ -98,30 +249,26 @@ _component = rule( def component( name, - units = None, tests = [], requirements = None, - components = None, + components = [], testonly = True, - visibility = None): + visibility = None, + tags = []): """Define a software component following S-CORE process guidelines. A component is a collection of related units that together provide a specific functionality. It consists of: - Component requirements: Requirements specification for the component - - Implementation: Concrete libraries/binaries that realize the component - - Units: Individual software units that implement the requirements + - Components: Nested components (for hierarchical structures) - Tests: Integration tests that verify the component as a whole Args: name: The name of the component. Used as the target name. - component_requirements: List of labels to component_requirements targets + requirements: List of labels to component_requirements targets that define the requirements for this component. - requirements: Alias for component_requirements (use one or the other). - implementation: List of labels to implementation targets (cc_library, - cc_binary, etc.) that realize this component. - units: List of labels to unit targets that comprise this component. - components: Alias for units (use one or the other). + components: List of labels to nested component targets (for hierarchical + component structures). tests: List of labels to Bazel test targets that verify the component integration. testonly: If true, only testonly targets can depend on this component. @@ -132,19 +279,19 @@ def component( component( name = "kvs_component", requirements = [":kvs_component_requirements"], - implementation = [":kvs_lib", ":kvs_tool"], - units = [":kvs_unit1", ":kvs_unit2"], + components = [":kvs_unit1", ":kvs_unit2"], tests = ["//persistency/kvs/tests:score_kvs_component_integration_tests"], visibility = ["//visibility:public"], ) ``` """ - _component( + _component_test( name = name, requirements = requirements, components = components, tests = tests, testonly = testonly, visibility = visibility, + tags = tags, ) diff --git a/bazel/rules/rules_score/private/dependability_analysis.bzl b/bazel/rules/rules_score/private/dependability_analysis.bzl index ae937234..4ed89d95 100644 --- a/bazel/rules/rules_score/private/dependability_analysis.bzl +++ b/bazel/rules/rules_score/private/dependability_analysis.bzl @@ -14,15 +14,39 @@ """ Dependability Analysis build rules for S-CORE projects. -This module provides macros and rules for defining dependability analysis -documentation following S-CORE process guidelines. Dependability analysis -combines safety analysis with dependent failure analysis (DFA) to provide -a comprehensive view of component reliability and safety. +A dependability analysis aggregates sub-analysis rules + + * **fmea** – ``fmea`` rule targets (failure modes, control + measures, and optional root cause FTA diagrams). + * **security_analysis** – security analysis rule targets (placeholder, + optional). """ +load("@lobster//:lobster.bzl", "subrule_lobster_html_report", "subrule_lobster_report") load("//bazel/rules/rules_score:providers.bzl", "AnalysisInfo", "ArchitecturalDesignInfo", "DependabilityAnalysisInfo", "SphinxSourcesInfo") +load("//bazel/rules/rules_score/private:lobster_config.bzl", "format_lobster_sources") + +# ============================================================================ +# Private Helpers +# ============================================================================ + +def _collect_analysis_providers(sa, rst_srcs_list, rst_deps_list, lobster_files): + """Collect analysis providers from a single sub-analysis target. -# DependabilityAnalysisInfo is re-exported from providers.bzl for backward compatibility. + Updates the provided lists/dicts in-place. + + Args: + sa: A sub-analysis target (fmea or security). + rst_srcs_list: List of depsets to extend with SphinxSourcesInfo.srcs. + rst_deps_list: List of depsets to extend with SphinxSourcesInfo.deps. + lobster_files: Dict to update with AnalysisInfo.lobster_files + (canonical name → File). + """ + if SphinxSourcesInfo in sa: + rst_srcs_list.append(sa[SphinxSourcesInfo].srcs) + rst_deps_list.append(sa[SphinxSourcesInfo].deps) + if AnalysisInfo in sa: + lobster_files.update(sa[AnalysisInfo].lobster_files) # ============================================================================ # Private Rule Implementation @@ -31,45 +55,133 @@ load("//bazel/rules/rules_score:providers.bzl", "AnalysisInfo", "ArchitecturalDe def _dependability_analysis_impl(ctx): """Implementation for dependability_analysis rule. - Collects dependability analysis artifacts including safety analysis results - and dependent failure analysis, linking them to architectural design. + Collects artefacts from all sub-analysis targets, generates the + combined lobster traceability config and report, and creates a + lobster-ci-report test executable. Args: - ctx: Rule context + ctx: Rule context. Returns: - List of providers including DefaultInfo and DependabilityAnalysisInfo + List of providers: DefaultInfo, DependabilityAnalysisInfo, + SphinxSourcesInfo. """ - security_analysis_files = depset(ctx.files.dfa) - safety_analysis_files = depset(ctx.files.fmea) + dfa_rst_files = depset(ctx.files.dfa) - # Get architectural design provider if available arch_design_info = None if ctx.attr.arch_design and ArchitecturalDesignInfo in ctx.attr.arch_design: arch_design_info = ctx.attr.arch_design[ArchitecturalDesignInfo] - # Combine all files for DefaultInfo - all_files = depset(transitive = [security_analysis_files, safety_analysis_files]) + rst_srcs_transitive = [dfa_rst_files] + rst_deps_transitive = [dfa_rst_files] + lobster_files = {} # canonical name → File, merged from all sub-analyses + + # ------------------------------------------------------------------------- + # Collect from fmea targets + # ------------------------------------------------------------------------- + fmea_output_files = [] + for sa in ctx.attr.fmea: + fmea_output_files.append(sa[DefaultInfo].files) + _collect_analysis_providers(sa, rst_srcs_transitive, rst_deps_transitive, lobster_files) - # Collect transitive sphinx sources from safety analysis and architectural design - transitive = [all_files] + # ------------------------------------------------------------------------- + # Collect from security_analysis targets + # ------------------------------------------------------------------------- + security_output_files = [] for sa in ctx.attr.security_analysis: - if SphinxSourcesInfo in sa: - transitive.append(sa[SphinxSourcesInfo].transitive_srcs) + security_output_files.append(sa[DefaultInfo].files) + _collect_analysis_providers(sa, rst_srcs_transitive, rst_deps_transitive, lobster_files) + + # Architectural design sphinx deps (optional) if ctx.attr.arch_design and SphinxSourcesInfo in ctx.attr.arch_design: - transitive.append(ctx.attr.arch_design[SphinxSourcesInfo].transitive_srcs) + rst_deps_transitive.append(ctx.attr.arch_design[SphinxSourcesInfo].deps) + + all_rst_srcs = depset(transitive = rst_srcs_transitive) + all_rst_deps = depset(transitive = rst_deps_transitive) + + # ========================================================================= + # Lobster traceability report (combined FM + CM + FTA) + # ========================================================================= + lobster_report_file = None + lobster_html_file = None + report_files = [] + + all_lobster_file_objects = lobster_files.values() + arch_lobster_files = arch_design_info.lobster_files.to_list() if arch_design_info else [] + all_lobster_file_objects = list(all_lobster_file_objects) + arch_lobster_files + if all_lobster_file_objects: + lobster_config = ctx.actions.declare_file( + "{}/traceability_config".format(ctx.label.name), + ) + + ctx.actions.expand_template( + template = ctx.file._lobster_sa_template, + output = lobster_config, + substitutions = { + "{ARCH_SOURCES}": format_lobster_sources(arch_lobster_files), + "{FM_SOURCES}": format_lobster_sources([lobster_files["failuremodes.lobster"]] if "failuremodes.lobster" in lobster_files else []), + "{CM_SOURCES}": format_lobster_sources([lobster_files["controlmeasures.lobster"]] if "controlmeasures.lobster" in lobster_files else []), + "{RC_SOURCES}": format_lobster_sources([lobster_files["root_causes.lobster"]] if "root_causes.lobster" in lobster_files else []), + }, + ) + + lobster_report_file = subrule_lobster_report(all_lobster_file_objects, lobster_config) + lobster_html_file = subrule_lobster_html_report(lobster_report_file) + + if lobster_report_file: + report_files.append(lobster_report_file) + if lobster_html_file: + report_files.append(lobster_html_file) + + # ========================================================================= + # Test executable (lobster-ci-report) + # ========================================================================= + test_executable = ctx.actions.declare_file( + "{}_lobster_ci_test_executable".format(ctx.attr.name), + ) + + runfiles_files = [] + if lobster_report_file: + ctx.actions.write( + output = test_executable, + content = "set -o pipefail; {} {}".format( + ctx.executable._lobster_ci_report.short_path, + lobster_report_file.short_path, + ), + ) + runfiles_files = [ctx.executable._lobster_ci_report, lobster_report_file] + else: + ctx.actions.write(output = test_executable, content = "exit 0") + + runfiles = ctx.runfiles(files = runfiles_files) + if lobster_report_file: + runfiles = runfiles.merge(ctx.attr._lobster_ci_report[DefaultInfo].default_runfiles) + + # ========================================================================= + # Build providers + # ========================================================================= + all_output_files = depset( + report_files, + transitive = [dfa_rst_files] + fmea_output_files + security_output_files, + ) return [ - DefaultInfo(files = all_files), + DefaultInfo( + runfiles = runfiles, + files = all_output_files, + executable = test_executable, + ), DependabilityAnalysisInfo( - safety_analysis = security_analysis_files, - security_analysis = security_analysis_files, + fmea = depset(transitive = fmea_output_files), + security_analysis = depset(transitive = security_output_files), + dfa = dfa_rst_files, arch_design = arch_design_info, name = ctx.label.name, + lobster_files = lobster_files, ), SphinxSourcesInfo( - srcs = all_files, - transitive_srcs = depset(transitive = transitive), + srcs = all_rst_srcs, + deps = all_rst_deps, ), ] @@ -77,32 +189,45 @@ def _dependability_analysis_impl(ctx): # Rule Definition # ============================================================================ -_dependability_analysis = rule( +_dependability_analysis_test = rule( implementation = _dependability_analysis_impl, - doc = "Collects dependability analysis documents for S-CORE process compliance", + doc = "Aggregates dependability analysis sub-analyses (fmea, security_analysis) " + + "and validates the combined traceability chain via lobster-ci-report.", attrs = { - "security_analysis": attr.label_list( - # TODO: change provider name + "fmea": attr.label_list( providers = [AnalysisInfo], mandatory = False, - doc = "List of safety_analysis targets containing FMEA, FMEDA, FTA results", + doc = "fmea rule targets (failure modes + control measures).", ), - "dfa": attr.label_list( - allow_files = [".rst", ".md"], + "security_analysis": attr.label_list( + providers = [AnalysisInfo], mandatory = False, - doc = "Dependent Failure Analysis (DFA) documentation", + doc = "Security analysis rule targets (placeholder -- not yet implemented).", ), - "fmea": attr.label_list( + "dfa": attr.label_list( allow_files = [".rst", ".md"], mandatory = False, - doc = "Failure Mode and Effects Analysis (FMEA) documentation", + doc = "Dependent Failure Analysis (DFA) documentation (placeholder).", ), "arch_design": attr.label( providers = [ArchitecturalDesignInfo], mandatory = False, - doc = "Reference to architectural_design target for traceability", + doc = "Reference to architectural_design target for interface tracing.", + ), + "_lobster_ci_report": attr.label( + default = "@lobster//:lobster-ci-report", + executable = True, + cfg = "exec", + doc = "lobster-ci-report executable for test execution.", + ), + "_lobster_sa_template": attr.label( + default = Label("//bazel/rules/rules_score/config:lobster_sa_template"), + allow_single_file = True, + doc = "Lobster config template for safety analysis traceability.", ), }, + subrules = [subrule_lobster_report, subrule_lobster_html_report], + test = True, ) # ============================================================================ @@ -111,59 +236,47 @@ _dependability_analysis = rule( def dependability_analysis( name, - safety_analysis = [], - dfa = [], fmea = [], + security_analysis = [], + dfa = [], arch_design = None, - visibility = None): + visibility = None, + tags = []): """Define dependability analysis following S-CORE process guidelines. - Dependability analysis provides a comprehensive view of component - reliability and safety by combining safety analysis results with - dependent failure analysis (DFA). It establishes traceability to - the architectural design for complete safety argumentation. + Aggregates up to three sub-analysis rules and validates the combined + traceability chain (FM + CM + FTA) via ``lobster-ci-report``. + + The target is a Bazel **test rule**. During ``bazel build`` it produces + aggregated documentation artefacts and the lobster traceability report. + During ``bazel test`` it runs ``lobster-ci-report`` to validate the chain. Args: - name: The name of the dependability analysis target. Used as the base - name for all generated targets. - safety_analysis: Optional list of labels to safety_analysis targets - containing the results of FMEA, FMEDA, FTA, or other safety - analysis methods as defined in the S-CORE process. - dfa: Optional list of labels to .rst or .md files containing - Dependent Failure Analysis (DFA) documentation. DFA identifies - failures that could affect multiple components or functions - as defined in the S-CORE process. - fmea: Optional list of labels to .rst or .md files containing - Failure Mode and Effects Analysis (FMEA) documentation. FMEA - identifies potential failure modes and their effects on the - system as defined in the S-CORE process. - arch_design: Optional label to an architectural_design target for - establishing traceability between dependability analysis and - the software architecture. - visibility: Bazel visibility specification for the generated targets. - - Generated Targets: - : Main dependability analysis target providing DependabilityAnalysisInfo + name: The name of the dependability analysis target. + fmea: Optional list of ``fmea`` rule target labels. + security_analysis: Optional list of security analysis rule target + labels (placeholder -- not yet implemented). + dfa: Optional list of ``.rst``/``.md`` DFA documentation files + (placeholder). + arch_design: Optional label to an ``architectural_design`` target + (placeholder). + visibility: Bazel visibility. + tags: Additional Bazel tags. Example: - ```starlark - dependability_analysis( - name = "my_dependability_analysis", - safety_analysis = [":my_safety_analysis"], - dfa = ["dependent_failure_analysis.rst"], - fmea = ["failure_mode_effects_analysis.rst"], - arch_design = ":my_architectural_design", - ) - ``` + .. code-block:: starlark + + dependability_analysis( + name = "my_da", + fmea = [":my_fmea"], + ) """ - _dependability_analysis( + _dependability_analysis_test( name = name, - # TODO: this needs to be fixed. A security is not a safety_analysis. - # we leave it for now for compatibility reasons until there is alignment on the a - # attributes of a dependability analysis - security_analysis = safety_analysis, - dfa = dfa, fmea = fmea, + security_analysis = security_analysis, + dfa = dfa, arch_design = arch_design, visibility = visibility, + tags = tags, ) diff --git a/bazel/rules/rules_score/private/dependable_element.bzl b/bazel/rules/rules_score/private/dependable_element.bzl index c90df37a..cf561577 100644 --- a/bazel/rules/rules_score/private/dependable_element.bzl +++ b/bazel/rules/rules_score/private/dependable_element.bzl @@ -20,12 +20,34 @@ element is a safety-critical component with comprehensive documentation includin assumptions of use, requirements, design, and safety analysis. """ +load( + "@lobster//:lobster.bzl", + "subrule_lobster_html_report", + "subrule_lobster_report", +) load( "//bazel/rules/rules_score:providers.bzl", + "ArchitecturalDesignInfo", + "CertifiedScope", "ComponentInfo", + "DependabilityAnalysisInfo", + "DependableElementInfo", + "DependableElementLobsterInfo", + "FeatureRequirementsInfo", + "SphinxModuleInfo", + "SphinxNeedsInfo", "SphinxSourcesInfo", "UnitInfo", ) +load( + "//bazel/rules/rules_score/private:architecture_aspect.bzl", + "CurrentArchitectureProviderInfo", + "collect_current_architecture_aspect", +) +load( + "//bazel/rules/rules_score/private:lobster_config.bzl", + "format_lobster_sources", +) load("//bazel/rules/rules_score/private:sphinx_module.bzl", "sphinx_module") # ============================================================================ @@ -35,10 +57,7 @@ load("//bazel/rules/rules_score/private:sphinx_module.bzl", "sphinx_module") _UNIT_DESIGN_SECTION_TEMPLATE = """Unit Design ----------- -.. toctree:: - :maxdepth: 2 - -{design_refs}""" +{design_includes}""" _IMPLEMENTATION_SECTION_TEMPLATE = """Implementation -------------- @@ -65,24 +84,35 @@ _COMPONENT_REQUIREMENTS_SECTION_TEMPLATE = """Component Requirements _COMPONENT_UNITS_SECTION_TEMPLATE = """Units ----- -This component is composed of the following units: +.. toctree:: + :maxdepth: 3 -{unit_links}""" +{unit_toctree_refs}""" _UNIT_TEMPLATE = """ -Unit: {unit_name} +{unit_name} {underline} {design_section}{implementation_section}{tests_section}""" _COMPONENT_TEMPLATE = """ -Component: {component_name} +{component_name} {underline} {requirements_section}{units_section}{implementation_section}{tests_section}""" +# ============================================================================ +# Integrity Level Definitions +# ============================================================================ + +# Allowed integrity levels in ascending order of stringency (A = lowest, D = highest). +_INTEGRITY_LEVELS = ["QM", "A", "B", "C", "D"] + +# Maps each integrity level to a numeric rank for comparison. +_INTEGRITY_LEVEL_RANK = {level: rank for rank, level in enumerate(_INTEGRITY_LEVELS)} + # ============================================================================ # Helper Functions for Documentation Generation # ============================================================================ @@ -99,7 +129,7 @@ def _filter_doc_files(files): Returns: List of documentation files """ - return [f for f in files if f.extension in ["rst", "md", "puml", "plantuml", "png", "svg"]] + return [f for f in files if f.extension in ["rst", "md", "puml", "plantuml", "png", "svg", "inc", "json"]] def _find_common_directory(files): """Find the longest common directory path for a list of files. @@ -376,7 +406,7 @@ def _generate_unit_doc(ctx, unit_target, unit_name): # Collect design files - unit_design depset contains File objects design_files = [] - design_refs = [] + design_include_lines = [] if unit_info.unit_design: doc_files = _filter_doc_files(unit_info.unit_design.to_list()) @@ -395,10 +425,22 @@ def _generate_unit_doc(ctx, unit_target, unit_name): design_files.append(output_file) if _is_document_file(f): - doc_ref = ("units/" + unit_name + "_design/" + relative_path) \ - .replace(".rst", "") \ - .replace(".md", "") - design_refs.append(" " + doc_ref) + # Inline the design RST directly into the unit page. + # Path is relative to the unit RST (which lives in units/). + include_path = unit_name + "_design/" + relative_path + design_include_lines.append(".. include:: " + include_path) + design_include_lines.append("") + elif f.extension in ("puml", "plantuml"): + # sphinxcontrib-plantuml resolves .. uml:: paths relative to + # the *including* document (units/unit_name.rst), not the + # included RST file. Symlink the PUML alongside the unit RST + # so the directive inside the included RST can find it. + # NOTE: All PUML files across all units must have unique + # basenames because they share the flat units/ namespace. + # Use unit-specific filenames (e.g. unit_1_class_diagram.puml) + # to avoid declaration conflicts. + sibling = _create_artifact_symlink(ctx, "units", f, f.basename) + design_files.append(sibling) # Collect implementation target names impl_names = [] @@ -410,16 +452,16 @@ def _generate_unit_doc(ctx, unit_target, unit_name): test_names = [] if unit_info.tests: for test in unit_info.tests.to_list(): - test_names.append(test.label) + test_names.append(test.short_path) # Generate RST content using template - underline = "=" * (len("Unit: " + unit_name)) + underline = "=" * len(unit_name) # Generate sections from template constants design_section = "" - if design_refs: + if design_include_lines: design_section = "\n" + _UNIT_DESIGN_SECTION_TEMPLATE.format( - design_refs = "\n".join(design_refs), + design_includes = "\n".join(design_include_lines), ) + "\n" implementation_section = "" @@ -471,11 +513,12 @@ def _generate_component_doc(ctx, comp_target, comp_name, unit_names): # Create RST file for this component comp_rst = ctx.actions.declare_file(ctx.label.name + "/components/" + comp_name + ".rst") - # Collect requirements files - requirements depset contains File objects + # Collect requirements files from SphinxSourcesInfo (ComponentInfo.requirements holds + # only lobster traceability files; Sphinx docs are carried via SphinxSourcesInfo) req_files = [] req_refs = [] - if comp_info.requirements: - doc_files = _filter_doc_files(comp_info.requirements.to_list()) + if SphinxSourcesInfo in comp_target: + doc_files = _filter_doc_files(comp_target[SphinxSourcesInfo].srcs.to_list()) if doc_files: # Find common directory @@ -492,19 +535,13 @@ def _generate_component_doc(ctx, comp_target, comp_name, unit_names): req_files.append(output_file) if _is_document_file(f): - doc_ref = ("components/" + comp_name + "_requirements/" + relative_path) \ + doc_ref = (comp_name + "_requirements/" + relative_path) \ .replace(".rst", "") \ .replace(".md", "") req_refs.append(" " + doc_ref) - # Collect test target names - test_names = [] - if comp_info.tests: - for test in comp_info.tests.to_list(): - test_names.append(test.label) - # Generate RST content using template - underline = "=" * (len("Component: " + comp_name)) + underline = "=" * len(comp_name) # Generate sections from template constants requirements_section = "" @@ -515,18 +552,12 @@ def _generate_component_doc(ctx, comp_target, comp_name, unit_names): units_section = "" if unit_names: - unit_links = "\n".join(["- :doc:`../units/" + unit_name + "`" for unit_name in unit_names]) + unit_toctree_refs = "\n".join([" ../units/" + name for name in unit_names]) units_section = "\n" + _COMPONENT_UNITS_SECTION_TEMPLATE.format( - unit_links = unit_links, + unit_toctree_refs = unit_toctree_refs, ) + "\n" tests_section = "" - if test_names: - test_list = "\n".join(["- ``" + str(test) + "``" for test in test_names]) - tests_section = "\n" + _TESTS_SECTION_TEMPLATE.format( - entity_type = "component", - test_list = test_list, - ) + "\n" # Generate component RST content from template constant component_content = _COMPONENT_TEMPLATE.format( @@ -545,6 +576,83 @@ def _generate_component_doc(ctx, comp_target, comp_name, unit_names): return (comp_rst, req_files) +# ============================================================================ +# Architecture Verification Helper Function +# ============================================================================ + +def _collect_architecture_components(ctx): + """Collect all architecture component entries for the dependable element. + + Gathers component/unit info from the collect_current_architecture_aspect on components and + adds a top-level entry for the dependable element itself. + + Args: + ctx: Rule context + + Returns: + Dict mapping component/unit names to their architecture entries + """ + + # Collect architecture info from aspect on components + all_components = {} + for comp in ctx.attr.components: + if CurrentArchitectureProviderInfo in comp: + info = comp[CurrentArchitectureProviderInfo] + all_components.update(info.components) + + # Build the top-level entry for the dependable element itself + de_components = [] + de_units = [] + for comp in ctx.attr.components: + if ComponentInfo in comp: + de_components.append(str(comp.label)) + elif UnitInfo in comp: + de_units.append(str(comp.label)) + + de_entry = {} + if de_components: + de_entry["components"] = de_components + if de_units: + de_entry["units"] = de_units + + # Guard against silently overwriting a key already collected by the aspect + if ctx.attr.module_name in all_components: + fail("module_name '{}' conflicts with an existing component key collected by the architecture aspect".format(ctx.attr.module_name)) + all_components[ctx.attr.module_name] = de_entry + + return all_components + +def _run_archver_validation(ctx, arch_json, static_fbs_files): + """Run the architecture verifier tool against a pre-built JSON file. + + Args: + ctx: Rule context + arch_json: The architecture JSON File object (already declared and written) + static_fbs_files: List of static FlatBuffer files to verify against + + Returns: + archver_log File object + """ + + archver_log = ctx.actions.declare_file(ctx.label.name + "/archver.log") + + archver_args = ctx.actions.args() + archver_args.add("--architecture-json", arch_json) + archver_args.add_all("--static-fbs", static_fbs_files) + archver_args.add("--output", archver_log) + + # ctx.actions.run will fail the build if archver returns non-zero exit code + ctx.actions.run( + inputs = [arch_json] + static_fbs_files, + outputs = [archver_log], + executable = ctx.executable._archver, + arguments = [archver_args], + progress_message = "Verifying architecture: %s" % ctx.label.name, + mnemonic = "ArchVerify", + ) + + return archver_log + # ============================================================================ # Index Generation Rule Implementation # ============================================================================ @@ -562,14 +670,18 @@ def _dependable_element_index_impl(ctx): DefaultInfo provider with generated index.rst file """ + # ========================================================================= + # Sphinx Documentation: process artifact files and generate RST pages + # ========================================================================= + # Declare output index file index_rst = ctx.actions.declare_file(ctx.label.name + "/index.rst") output_files = [index_rst] - # Define artifacts - # Note: "requirements" can contain both component_requirements and feature_requirements + # Process each well-known artifact type into symlinked output files and + # toctree references for the index template. + # "requirements" covers both feature_requirements and component_requirements. artifact_types = [ - "components", "assumptions_of_use", "requirements", "architectural_design", @@ -577,7 +689,6 @@ def _dependable_element_index_impl(ctx): "checklists", ] - # Process each artifact type artifacts_by_type = {} for artifact_name in artifact_types: files, refs = _process_artifact_type(ctx, artifact_name) @@ -587,22 +698,24 @@ def _dependable_element_index_impl(ctx): # Collect all units recursively from components all_units = _collect_units_recursive(ctx.attr.components) - # Generate documentation for each unit - unit_refs = [] + # Generate a dedicated RST page for each unit. Unit pages are referenced + # via toctree from the component RST page (see _generate_component_doc). for unit_name, unit_target in all_units.items(): unit_rst, unit_files = _generate_unit_doc(ctx, unit_target, unit_name) output_files.append(unit_rst) output_files.extend(unit_files) - unit_refs.append(" units/" + unit_name) - # Generate documentation for each component + # Generate a dedicated RST page for each component and collect certification + # metadata (certified scopes, dependent labels) for later validation. component_refs = [] + collected_certified_scopes = [] + collected_dependent_labels = [] for comp_target in ctx.attr.components: if ComponentInfo in comp_target: comp_info = comp_target[ComponentInfo] comp_name = comp_info.name - # Collect units that belong to this component + # Collect direct unit names for this component's RST page comp_unit_names = [] for nested in comp_info.components.to_list(): if UnitInfo in nested: @@ -615,12 +728,37 @@ def _dependable_element_index_impl(ctx): comp_rst, comp_files = _generate_component_doc(ctx, comp_target, comp_name, comp_unit_names) output_files.append(comp_rst) output_files.extend(comp_files) - component_refs.append(" components/" + comp_name) + component_refs.append(comp_name) + + if comp_info.dependent_labels: + collected_dependent_labels.append(comp_info.dependent_labels) + if CertifiedScope in comp_target: + collected_certified_scopes.append(comp_target[CertifiedScope].transitive_scopes) + + # Collect CertifiedScope from processed_deps as well (other dependable elements) + for dep in ctx.attr.processed_deps: + if CertifiedScope in dep: + collected_certified_scopes.append(dep[CertifiedScope].transitive_scopes) + + # Generate an intermediate components/index.rst that groups all component pages + # under a single "Components" navigation entry in the Sphinx sidebar. + if component_refs: + comp_index_rst = ctx.actions.declare_file(ctx.label.name + "/components/index.rst") + comp_index_underline = "=" * len("Components") + comp_toctree_entries = "\n ".join(component_refs) + ctx.actions.write( + output = comp_index_rst, + content = "Components\n" + comp_index_underline + "\n\n.. toctree::\n :maxdepth: 2\n\n " + comp_toctree_entries + "\n", + ) + output_files.append(comp_index_rst) + components_ref = "components/index" + else: + components_ref = "" - # Process dependencies (submodules) + # Generate submodule links for the index page deps_links = _process_deps(ctx) - # Generate index file from template + # Render the index.rst using the template title = ctx.attr.module_name underline = "=" * len(title) @@ -631,10 +769,9 @@ def _dependable_element_index_impl(ctx): "{title}": title, "{underline}": underline, "{description}": ctx.attr.description, - "{units}": "\n".join(unit_refs) if unit_refs else " (none)", - "{components}": "\n".join(component_refs) if component_refs else " (none)", + "{components}": components_ref, "{assumptions_of_use}": "\n ".join(artifacts_by_type["assumptions_of_use"]), - "{component_requirements}": "\n ".join(artifacts_by_type["requirements"]), + "{requirements}": "\n ".join(artifacts_by_type["requirements"]), "{architectural_design}": "\n ".join(artifacts_by_type["architectural_design"]), "{dependability_analysis}": "\n ".join(artifacts_by_type["dependability_analysis"]), "{checklists}": "\n ".join(artifacts_by_type["checklists"]), @@ -642,8 +779,189 @@ def _dependable_element_index_impl(ctx): }, ) + # ========================================================================= + # Architecture Verification: build current-architecture JSON and run archver + # ========================================================================= + + # Collect the current architecture from all components (via aspect) and + # write it as JSON consumed by the architecture verifier tool. + all_components = _collect_architecture_components(ctx) + + arch_json = ctx.actions.declare_file(ctx.label.name + "/architecture.json") + ctx.actions.write( + output = arch_json, + content = json.encode_indent({"components": all_components}, indent = " "), + ) + + # Collect static FlatBuffers from architectural_design targets (the expected + # static architecture) and verify them against the current architecture. + static_fbs_files = [] + for ad in ctx.attr.architectural_design: + if ArchitecturalDesignInfo in ad: + static_fbs_files.extend(ad[ArchitecturalDesignInfo].static.to_list()) + + # Run architecture verifier; build fails automatically on non-zero exit + archver_log = _run_archver_validation(ctx, arch_json, static_fbs_files) + + # Both outputs are included so archver always runs in a default build. + # archver_log is also exposed in the debug output group for explicit access. + output_files.append(arch_json) + output_files.append(archver_log) + + # ========================================================================= + # Safety Certification Validation: certified scope and integrity level checks + # ========================================================================= + + # Verify that all transitive implementation dependencies are within the + # certified scope declared for this dependable element. + # @todo: Make this check aware of the repository (@foo) for example. + certified_scopes = depset(transitive = collected_certified_scopes).to_list() + tree = {} + for certified_scope in certified_scopes: + certified_scope = Label(certified_scope) + node = tree + path = certified_scope.package.split("/") + last_element = path.pop() + for path in path: + node = node.setdefault(path, default = {}) + + if type(node) == type([]): + node.append(certified_scope.name) + else: + inserted_element = node.setdefault(last_element, default = [certified_scope.name]) + if inserted_element != [certified_scope.name]: + fail("The same scope is covered twice: {}".format(certified_scope)) + + dependencies = depset(transitive = collected_dependent_labels).to_list() + for dep in dependencies: + node = tree + for path in dep.package.split("/"): + if type(node) == type([]): + if "__subpackages__" in node: + break + elif dep.name in node: + break + else: + fail("Not in certified scope {}, stopping at {}".format(dep, path)) + + child = node.get(path) + if child == None: + fail("Not in certified scope {}, stopping at {}".format(dep, path)) + else: + node = child + + # Integrity-level check: a dependable element must not depend on elements + # with a lower integrity level than its own (D > C > B > A). + own_rank = _INTEGRITY_LEVEL_RANK[ctx.attr.integrity_level] + for dep in ctx.attr.processed_deps: + if DependableElementInfo in dep: + dep_info = dep[DependableElementInfo] + dep_rank = _INTEGRITY_LEVEL_RANK[dep_info.integrity_level] + if dep_rank < own_rank: + fail( + "Integrity level violation: '{}' (level {}) depends on '{}' (level {}). " + + "A dependable element must not depend on elements with a lower integrity level.", + ctx.label, + ctx.attr.integrity_level, + dep_info.label, + dep_info.integrity_level, + ) + + # ========================================================================= + # Lobster Traceability: Dependable Element Level + # Builds a three-tier report: Feature Requirements <- Component Requirements + # <- Unit Tests (gtest). The report is only produced when all three tiers + # are present; gtest is optional (component-only traceability is possible). + # ========================================================================= + + # Collect feature requirement .lobster files from requirements targets + feat_req_lobster_files = [] + for req_target in ctx.attr.requirements: + if FeatureRequirementsInfo in req_target: + feat_req_lobster_files.append(req_target[FeatureRequirementsInfo].srcs) + + feat_req_lobster_depset = depset(transitive = feat_req_lobster_files) + + # Collect component requirement and test .lobster files from ComponentInfo + comp_req_lobster_files = [] + comp_test_lobster_files = [] + comp_arch_lobster_files = [] + for comp_target in ctx.attr.components: + if ComponentInfo in comp_target: + comp_info = comp_target[ComponentInfo] + if comp_info.requirements: + comp_req_lobster_files.append(comp_info.requirements) + if comp_info.tests: + comp_test_lobster_files.append(comp_info.tests) + if comp_info.architecture: + comp_arch_lobster_files.append(comp_info.architecture) + + comp_req_lobster_depset = depset(transitive = comp_req_lobster_files) + comp_test_lobster_depset = depset(transitive = comp_test_lobster_files) + comp_arch_lobster_depset = depset(transitive = comp_arch_lobster_files) + + # Collect safety analysis lobster files from dependability_analysis targets + sa_lobster_files = {} # canonical name -> File, merged from all DA targets + for da_target in ctx.attr.dependability_analysis: + if DependabilityAnalysisInfo in da_target: + da_info = da_target[DependabilityAnalysisInfo] + sa_lobster_files.update(da_info.lobster_files) + + # Collect public api lobster files from architectural_design targets + public_api_lobster_list = [] + for ad_target in ctx.attr.architectural_design: + if ArchitecturalDesignInfo in ad_target: + public_api_lobster_list += ad_target[ArchitecturalDesignInfo].public_api_lobster_files.to_list() + + # Build the DE-level lobster report if feature and component traces exist + feat_req_list = feat_req_lobster_depset.to_list() + comp_req_list = comp_req_lobster_depset.to_list() + comp_test_list = comp_test_lobster_depset.to_list() + comp_arch_list = comp_arch_lobster_depset.to_list() + interface_req_list = public_api_lobster_list + fm_list = [sa_lobster_files["failuremodes.lobster"]] if "failuremodes.lobster" in sa_lobster_files else [] + cm_list = [sa_lobster_files["controlmeasures.lobster"]] if "controlmeasures.lobster" in sa_lobster_files else [] + rc_list = [sa_lobster_files["root_causes.lobster"]] if "root_causes.lobster" in sa_lobster_files else [] + + lobster_report_file = None + lobster_html_report = None + lobster_files = [] + if feat_req_list and comp_req_list: + lobster_config = ctx.actions.declare_file(ctx.label.name + "/de_traceability_config") + ctx.actions.expand_template( + template = ctx.file._lobster_de_template, + output = lobster_config, + substitutions = { + "{FEAT_REQ_SOURCES}": format_lobster_sources(feat_req_list), + "{COMP_REQ_SOURCES}": format_lobster_sources(comp_req_list), + "{ARCH_SOURCES}": format_lobster_sources(comp_arch_list), + "{UNIT_TEST_SOURCES}": format_lobster_sources(comp_test_list), + "{PUBLIC_API_SOURCES}": format_lobster_sources(interface_req_list), + "{FM_SOURCES}": format_lobster_sources(fm_list), + "{CM_SOURCES}": format_lobster_sources(cm_list), + "{RC_SOURCES}": format_lobster_sources(rc_list), + }, + ) + + all_lobster_inputs = feat_req_list + comp_req_list + comp_arch_list + comp_test_list + interface_req_list + fm_list + cm_list + rc_list + lobster_report_file = subrule_lobster_report(all_lobster_inputs, lobster_config) + lobster_html_report = subrule_lobster_html_report(lobster_report_file) + + lobster_files = [lobster_config, lobster_report_file, lobster_html_report] + output_files.extend(lobster_files) + return [ DefaultInfo(files = depset(output_files)), + CertifiedScope(transitive_scopes = depset(transitive = collected_certified_scopes)), + DependableElementInfo( + integrity_level = ctx.attr.integrity_level, + label = ctx.label, + ), + DependableElementLobsterInfo( + lobster_report = lobster_report_file, + lobster_html_report = lobster_html_report, + ), + OutputGroupInfo(debug = depset([archver_log])), ] _dependable_element_index = rule( @@ -664,7 +982,8 @@ _dependable_element_index = rule( ), "requirements": attr.label_list( mandatory = True, - doc = "Requirements targets (component_requirements, feature_requirements, etc.).", + providers = [FeatureRequirementsInfo], + doc = "Feature requirements targets (feature_requirements only).", ), "architectural_design": attr.label_list( mandatory = True, @@ -675,8 +994,9 @@ _dependable_element_index = rule( doc = "Dependability analysis targets or files.", ), "components": attr.label_list( - default = [], - doc = "Safety checklists targets or files.", + mandatory = True, + aspects = [collect_current_architecture_aspect], + doc = "Component targets (aspect is applied here and passed to subrule).", ), "tests": attr.label_list( default = [], @@ -695,13 +1015,126 @@ _dependable_element_index = rule( default = [], doc = "Dependencies on other dependable element modules (submodules).", ), + "processed_deps": attr.label_list( + default = [], + doc = "Dependencies on other dependable element modules (submodules).", + ), + "integrity_level": attr.string( + mandatory = True, + values = _INTEGRITY_LEVELS, + doc = "Integrity level of the dependable element. Allowed values: 'A', 'B', 'C', 'D' (D > C > B > A).", + ), + "_archver": attr.label( + default = Label("//validation/archver"), + executable = True, + cfg = "exec", + doc = "Architecture verifier tool", + ), + "_lobster_de_template": attr.label( + default = Label("//bazel/rules/rules_score/config:lobster_de_template"), + allow_single_file = True, + doc = "Lobster config template for dependable element traceability.", + ), }, + subrules = [subrule_lobster_report, subrule_lobster_html_report], ) # ============================================================================ -# Public Macro +# Main Dependable Element Rule (test) # ============================================================================ +def _dependable_element_impl(ctx): + """Implementation for the main dependable_element test rule. + + * Forwards SphinxModuleInfo / SphinxNeedsInfo so other modules can use + this target directly as a Sphinx dependency. + * Forwards CertifiedScope and DependableElementInfo for safety checks. + * Builds a test executable that runs lobster-ci-report on the + pre-built lobster JSON report, so ``bazel test `` validates + traceability. + """ + sphinx_dep = ctx.attr.sphinx_module_dep + index_dep = ctx.attr.index_dep + + # --- Lobster traceability test executable -------------------------------- + lobster_info = index_dep[DependableElementLobsterInfo] + test_executable = ctx.actions.declare_file(ctx.label.name + "_lobster_test") + + if lobster_info.lobster_report != None: + command = "set -o pipefail; {ci} {report}".format( + ci = ctx.executable._lobster_ci_report.short_path, + report = lobster_info.lobster_report.short_path, + ) + runfiles = ctx.runfiles( + files = [ctx.executable._lobster_ci_report, lobster_info.lobster_report], + ).merge(ctx.attr._lobster_ci_report[DefaultInfo].default_runfiles) + else: + command = "exit 0" + runfiles = ctx.runfiles() + + ctx.actions.write( + output = test_executable, + content = command, + is_executable = True, + ) + + # Compose default outputs: the two lobster report files (JSON + HTML) and + # the Sphinx HTML documentation. Intermediate files from the index (RST + # sources, lobster config, etc.) are intentionally excluded. + lobster_default_files = [] + if lobster_info.lobster_report: + lobster_default_files.append(lobster_info.lobster_report) + if lobster_info.lobster_html_report: + lobster_default_files.append(lobster_info.lobster_html_report) + + return [ + # DefaultInfo: two lobster report files + Sphinx HTML docs so that + # ``bazel build `` produces exactly the final user-facing outputs. + DefaultInfo( + executable = test_executable, + files = depset(lobster_default_files, transitive = [sphinx_dep[DefaultInfo].files]), + runfiles = runfiles, + ), + # Sphinx docs providers: forwarded from sphinx_module so callers can use + # directly as a Sphinx dependency + sphinx_dep[SphinxModuleInfo], + # Safety providers: forwarded from index for certification scope and + # integrity-level checks by parent dependable elements + index_dep[CertifiedScope], + index_dep[DependableElementInfo], + ] + ([sphinx_dep[SphinxNeedsInfo]] if SphinxNeedsInfo in sphinx_dep else []) + +_dependable_element_test = rule( + implementation = _dependable_element_impl, + doc = """Main dependable element target. + + Wraps the sphinx_module and exposes a lobster traceability test. + Running ``bazel test `` executes lobster-ci-report to verify + that all traceability links are satisfied. + """, + attrs = { + "index_dep": attr.label( + mandatory = True, + doc = "The _index target generated by _dependable_element_index.", + ), + "sphinx_module_dep": attr.label( + mandatory = True, + doc = "The _doc sphinx_module target providing SphinxModuleInfo.", + ), + "_lobster_ci_report": attr.label( + default = Label("@lobster//:lobster-ci-report"), + executable = True, + cfg = "exec", + doc = "Lobster CI report tool used to validate traceability at test time.", + ), + }, + test = True, +) + +# ============================================================================ +# Public Macro +# ============================================================================ +# lobster-trace: Tools.ArchitectureModelingDependableElement def dependable_element( name, description, @@ -711,8 +1144,10 @@ def dependable_element( dependability_analysis, components, tests, + integrity_level, checklists = [], deps = [], + sphinx = Label("//bazel/rules/rules_score:score_build"), testonly = True, visibility = None): """Define a dependable element (Safety Element out of Context - SEooC) following S-CORE process guidelines. @@ -743,6 +1178,9 @@ def dependable_element( this dependable element. tests: List of labels to Bazel test targets that verify the dependable element at the system level (integration tests, system tests). + integrity_level: Integrity level of the dependable element. Allowed values: + 'A', 'B', 'C', 'D' (D > C > B > A). A dependable element must not + depend on elements with a lower integrity level than its own. checklists: Optional list of labels to .rst or .md files containing safety checklists and verification documents. deps: Optional list of other module targets this element depends on. @@ -758,12 +1196,17 @@ def dependable_element( """ + processed_deps = [] + for dep in deps: + processed_deps.append("{}_index".format(dep)) + # Step 1: Generate index.rst and collect all artifacts + # Note: archver validation runs as a subrule within the index generation _dependable_element_index( name = name + "_index", module_name = name, description = description, - template = Label("//bazel/rules/rules_score:templates/seooc_index.template.rst"), + template = Label("//bazel/rules/rules_score:templates/dependable_element_index.template.rst"), assumptions_of_use = assumptions_of_use, requirements = requirements, components = components, @@ -772,16 +1215,32 @@ def dependable_element( checklists = checklists, tests = tests, deps = deps, + processed_deps = processed_deps, + integrity_level = integrity_level, testonly = testonly, - visibility = ["//visibility:private"], + visibility = visibility, ) - # Step 2: Create sphinx_module using generated index and artifacts + # Step 2: Create sphinx_module using generated index and artifacts. + # Internal deps use the _doc variant so that sphinx_module can resolve + # _doc (SphinxModuleInfo) and _doc_needs (SphinxNeedsInfo). sphinx_module( - name = name, + name = name + "_doc", srcs = [":" + name + "_index"], index = ":" + name + "_index", - deps = deps, + deps = [d + "_doc" for d in deps], + sphinx = sphinx, testonly = testonly, visibility = visibility, ) + + # Step 3: Create the main target: + # - is a test rule (bazel test runs the lobster check) + # - forwards SphinxModuleInfo / SphinxNeedsInfo so callers can use it as a + # sphinx dependency without knowing about the internal _doc split + _dependable_element_test( + name = name, + index_dep = ":" + name + "_index", + sphinx_module_dep = ":" + name + "_doc", + visibility = visibility, + ) diff --git a/bazel/rules/rules_score/private/fmea.bzl b/bazel/rules/rules_score/private/fmea.bzl index 4dfe7bcd..c1403cfd 100644 --- a/bazel/rules/rules_score/private/fmea.bzl +++ b/bazel/rules/rules_score/private/fmea.bzl @@ -14,68 +14,279 @@ """ FMEA (Failure Mode and Effects Analysis) build rules for S-CORE projects. -This module provides macros and rules for defining FMEA documentation -following S-CORE process guidelines. FMEA documents failure modes, control -measures, and root-cause fault tree analysis diagrams for a component. +The rule generates a single ``fmea.rst`` page (expanded from a +template) with three inline sections: + + 1. **Failure Modes** – TRLC failure-mode targets are rendered to ``.inc`` + files by trlc_rst and pulled in via ``.. include::``. + 2. **Control Measures** – TRLC control-measure targets rendered to ``.inc`` + the same way as failure modes. + 3. **Root Causes** – optional FTA PlantUML diagrams (``.puml`` / + ``.plantuml``) given via the ``root_causes`` attribute. Each diagram + is preprocessed to inline ``fta_metamodel.puml`` (making it + self-contained) and then referenced via ``.. uml::`` inside the page. + Lobster traceability items are extracted to ``{label}/root_causes.lobster``. + +Using ``.inc`` (not ``.rst``) for the helper include files keeps them out of +the Sphinx toctree (``_is_document_file`` only matches ``.rst``/``.md``) while +``_filter_doc_files`` in ``dependable_element.bzl`` still symlinks them +alongside ``fmea.rst`` so ``.. include::`` resolves at build time. + +``AnalysisInfo`` carries all lobster traceability files (failuremodes, +controlmeasures, and root_causes if present) as a ``lobster_files`` dict +keyed by canonical filename (e.g. ``{"failuremodes.lobster": File, ...}``). +All Sphinx source files travel via ``SphinxSourcesInfo``. + +This is a **build-only** rule. The combined traceability *test* is owned +by the ``dependability_analysis`` rule which wraps this one. """ +load("@trlc//:trlc.bzl", "TrlcProviderInfo") load("//bazel/rules/rules_score:providers.bzl", "AnalysisInfo", "ArchitecturalDesignInfo", "SphinxSourcesInfo") -# AnalysisInfo and ArchitecturalDesignInfo are re-exported from providers.bzl for backward compatibility. +# ============================================================================ +# Root-cause (FTA) processing helper +# ============================================================================ + +def _process_root_causes(ctx): + """Preprocess FTA diagrams (inline metamodel) and extract lobster items in one action. + + Args: + ctx: Rule context. Reads ``ctx.files.root_causes``, + ``ctx.file._fta_metamodel``, and + ``ctx.executable._safety_analysis_tools``. + + Returns: + Tuple ``(preprocessed_diagrams, [root_causes_lobster], rst_section_text)``. + All lists are empty and the section text is ``""`` when there are no + PlantUML root-cause inputs. + """ + puml_inputs = [ + f + for f in ctx.files.root_causes + if f.extension in ("puml", "plantuml") + ] + if not puml_inputs: + return [], [], "" + + # Declare one preprocessed output per input diagram (same directory). + preprocessed_diagrams = [ + ctx.actions.declare_file("{}/{}".format(ctx.label.name, src.basename)) + for src in puml_inputs + ] + root_causes_lobster = ctx.actions.declare_file( + "{}/root_causes.lobster".format(ctx.label.name), + ) + + # Single action: preprocess every diagram and extract lobster traceability. + output_dir = preprocessed_diagrams[0].dirname + args = ctx.actions.args() + args.add("--metamodel", ctx.file._fta_metamodel) + args.add("--output-dir", output_dir) + args.add("--lobster", root_causes_lobster) + args.add_all(puml_inputs) + ctx.actions.run( + inputs = puml_inputs + [ctx.file._fta_metamodel], + outputs = preprocessed_diagrams + [root_causes_lobster], + executable = ctx.executable._safety_analysis_tools, + arguments = [args], + progress_message = "Processing root cause FTA diagrams for %s" % ctx.label.name, + ) + + # Build the RST fragment that fmea.rst embeds. + diagrams_rst = "\n\n".join( + [".. uml:: " + diagram.basename for diagram in preprocessed_diagrams], + ) + root_causes_rst_section = "Root Causes\n-----------\n\n{}".format(diagrams_rst) + + return preprocessed_diagrams, [root_causes_lobster], root_causes_rst_section # ============================================================================ -# Private Rule Implementation +# Private Helpers # ============================================================================ -def _fmea_impl(ctx): - """Implementation for fmea rule. +def _render_trlc_inc(ctx, src, suffix): + """Render a trlc_requirements target to an ``.inc`` file via trlc_rst. - Collects FMEA artifacts including failure modes, control measures, and - fault tree diagrams, linking them to architectural design. + The ``.inc`` extension means the file is symlinked into the output + directory (via ``_filter_doc_files``) but is NOT added to any Sphinx + toctree (``_is_document_file`` only matches ``.rst`` / ``.md``). Args: - ctx: Rule context + ctx: Rule context. + src: Label carrying TrlcProviderInfo. + suffix: Suffix appended to the target name before ``.inc``. Returns: - List of providers including DefaultInfo, AnalysisInfo, SphinxSourcesInfo + Declared ``.inc`` output File inside ``{label.name}/``. """ - controlmeasures = depset(ctx.files.controlmeasures) - failuremodes = depset(ctx.files.failuremodes) - fta = depset(ctx.files.root_causes) + trlc_provider = src[TrlcProviderInfo] + rendered = ctx.actions.declare_file( + "{}/{}{}.inc".format(ctx.label.name, src.label.name, suffix), + ) + args = ctx.actions.args() + args.add("--output", rendered.path) + args.add("--input-dir", ".") + args.add("--title", "") + args.add("--source-files") + args.add_all(trlc_provider.reqs) + ctx.actions.run( + inputs = src[DefaultInfo].files, + outputs = [rendered], + arguments = [args], + executable = ctx.executable._renderer, + ) + return rendered - # TODO: render requirement sources (failuremodes, controlmeasures) into - # documentation and extract traceability artifacts. +# ============================================================================ +# Private Rule Implementation +# ============================================================================ - # TODO: preprocess fault tree diagrams (root_causes) and extract - # traceability artifacts. +def _fmea_impl(ctx): + output_files = [] - # Get architectural design provider if available - arch_design_info = None - if ctx.attr.arch_design and ArchitecturalDesignInfo in ctx.attr.arch_design: - arch_design_info = ctx.attr.arch_design[ArchitecturalDesignInfo] + # ------------------------------------------------------------------------- + # 0. Process root causes (FTA diagrams) if provided + # ------------------------------------------------------------------------- + preprocessed_diagrams, root_cause_lobster_files, root_causes_rst_section = _process_root_causes(ctx) + output_files.extend(preprocessed_diagrams) - # Combine all files for DefaultInfo - all_files = depset( - transitive = [controlmeasures, failuremodes, fta], + # ------------------------------------------------------------------------- + # 1. Render failure modes: TRLC -> .inc via trlc_rst + # ------------------------------------------------------------------------- + failuremodes_inc = [ + _render_trlc_inc(ctx, src, "_failuremodes") + for src in ctx.attr.failuremodes + ] + output_files.extend(failuremodes_inc) + + # ------------------------------------------------------------------------- + # 2. Render control measures: TRLC -> .inc via trlc_rst + # ------------------------------------------------------------------------- + controlmeasures_inc = [ + _render_trlc_inc(ctx, src, "_controlmeasures") + for src in ctx.attr.controlmeasures + ] + output_files.extend(controlmeasures_inc) + + # ------------------------------------------------------------------------- + # 3. Run lobster-trlc on TRLC sources -> lobster files + # Use TrlcProviderInfo.reqs to check if there are any TRLC sources to + # process. Pass DefaultInfo.files as sandbox inputs so that the .rsl + # spec files (needed to resolve `import ScoreReq` etc.) are available + # alongside the .trlc record files. + # ------------------------------------------------------------------------- + failure_mode_trlc_srcs = [] + failure_mode_inputs = [] + for src in ctx.attr.failuremodes: + failure_mode_trlc_srcs.extend(src[TrlcProviderInfo].reqs.to_list()) + failure_mode_inputs.extend(src[DefaultInfo].files.to_list()) + + failuremodes_lobster_files = [] + if failure_mode_trlc_srcs: + failuremodes_lobster = ctx.actions.declare_file( + "{}/failuremodes.lobster".format(ctx.label.name), + ) + args = ctx.actions.args() + args.add("--config", ctx.file._fm_lobster_config.path) + args.add("--out", failuremodes_lobster.path) + ctx.actions.run( + inputs = failure_mode_inputs + [ctx.file._fm_lobster_config], + outputs = [failuremodes_lobster], + executable = ctx.executable._lobster_trlc, + arguments = [args], + progress_message = "lobster-trlc {}".format(failuremodes_lobster.path), + ) + failuremodes_lobster_files.append(failuremodes_lobster) + + control_measure_trlc_srcs = [] + control_measure_inputs = [] + for src in ctx.attr.controlmeasures: + control_measure_trlc_srcs.extend(src[TrlcProviderInfo].reqs.to_list()) + control_measure_inputs.extend(src[DefaultInfo].files.to_list()) + + controlmeasures_lobster_files = [] + if control_measure_trlc_srcs: + controlmeasures_lobster = ctx.actions.declare_file( + "{}/controlmeasures.lobster".format(ctx.label.name), + ) + args = ctx.actions.args() + args.add("--config", ctx.file._cm_lobster_config.path) + args.add("--out", controlmeasures_lobster.path) + ctx.actions.run( + inputs = control_measure_inputs + [ctx.file._cm_lobster_config], + outputs = [controlmeasures_lobster], + executable = ctx.executable._lobster_trlc, + arguments = [args], + progress_message = "lobster-trlc {}".format(controlmeasures_lobster.path), + ) + controlmeasures_lobster_files.append(controlmeasures_lobster) + + # ------------------------------------------------------------------------- + # 4. Generate fmea.rst via expand_template + # ------------------------------------------------------------------------- + fmea_rst = ctx.actions.declare_file( + "{}/fmea.rst".format(ctx.label.name), + ) + + title = ctx.label.name + + failure_modes_rst_includes = "\n\n".join( + [".. include:: " + f.basename for f in failuremodes_inc], + ) + control_measures_rst_includes = "\n\n".join( + [".. include:: " + f.basename for f in controlmeasures_inc], + ) + + failure_modes_section = "" + if failuremodes_inc: + failure_modes_section = "Failure Modes\n-------------\n\n" + failure_modes_rst_includes + + control_measures_section = "" + if controlmeasures_inc: + control_measures_section = "Control Measures\n----------------\n\n" + control_measures_rst_includes + + ctx.actions.expand_template( + template = ctx.file._template, + output = fmea_rst, + substitutions = { + "{title}": title, + "{underline}": "=" * len(title), + "{failure_modes_section}": failure_modes_section, + "{control_measures_section}": control_measures_section, + "{root_causes_section}": root_causes_rst_section, + }, ) + output_files.append(fmea_rst) + + # ------------------------------------------------------------------------- + # 5. Build providers + # ------------------------------------------------------------------------- + lobster_files = {} + for f in failuremodes_lobster_files: + lobster_files["failuremodes.lobster"] = f + for f in controlmeasures_lobster_files: + lobster_files["controlmeasures.lobster"] = f + for f in root_cause_lobster_files: + lobster_files["root_causes.lobster"] = f + + all_sphinx_srcs = depset(output_files) - # Collect transitive sphinx sources from architectural design - transitive = [all_files] + sphinx_deps = [all_sphinx_srcs] if ctx.attr.arch_design and SphinxSourcesInfo in ctx.attr.arch_design: - transitive.append(ctx.attr.arch_design[SphinxSourcesInfo].transitive_srcs) + sphinx_deps.append(ctx.attr.arch_design[SphinxSourcesInfo].deps) return [ - DefaultInfo(files = all_files), + DefaultInfo( + files = depset(output_files), + ), AnalysisInfo( - controlmeasures = controlmeasures, - failuremodes = failuremodes, - fta = fta, - arch_design = arch_design_info, name = ctx.label.name, + lobster_files = lobster_files, ), SphinxSourcesInfo( - srcs = all_files, - transitive_srcs = depset(transitive = transitive), + srcs = all_sphinx_srcs, + deps = depset(transitive = sphinx_deps), ), ] @@ -85,27 +296,70 @@ def _fmea_impl(ctx): _fmea = rule( implementation = _fmea_impl, - doc = "Collects FMEA documents and diagrams for S-CORE process compliance", + doc = "Renders FMEA TRLC sources to .inc files and generates lobster traceability files. " + + "Build-only rule; traceability testing is owned by dependability_analysis.", attrs = { "failuremodes": attr.label_list( - allow_files = [".rst", ".md", ".trlc"], + providers = [TrlcProviderInfo], mandatory = False, - doc = "Failure modes documentation or requirements targets", + doc = "Failure modes as trlc_requirements targets (rendered to .inc via trlc_rst).", ), "controlmeasures": attr.label_list( - allow_files = [".rst", ".md", ".trlc"], + providers = [TrlcProviderInfo], mandatory = False, - doc = "Control measures documentation or requirements targets (can be AoUs or requirements)", + doc = "Control measures as trlc_requirements targets (rendered to .inc via trlc_rst).", ), "root_causes": attr.label_list( - allow_files = [".puml", ".plantuml", ".png", ".svg"], + allow_files = [".puml", ".plantuml"], mandatory = False, - doc = "Root-cause Fault Tree Analysis (FTA) diagrams", + doc = "Root cause FTA PlantUML diagram files. " + + "``fta_metamodel.puml`` is inlined automatically; " + + "lobster items are extracted to ``root_causes.lobster``.", ), "arch_design": attr.label( providers = [ArchitecturalDesignInfo], mandatory = False, - doc = "Reference to architectural_design target for traceability", + doc = "Reference to architectural_design target for traceability.", + ), + "_safety_analysis_tools": attr.label( + default = Label("//bazel/rules/rules_score:safety_analysis_tools"), + executable = True, + allow_files = True, + cfg = "exec", + doc = "safety_analysis_tools binary: preprocess and extract subcommands.", + ), + "_fta_metamodel": attr.label( + default = Label("//plantuml:fta_metamodel"), + allow_single_file = True, + doc = "fta_metamodel.puml whose content is inlined into root cause diagrams.", + ), + "_renderer": attr.label( + default = Label("@trlc//tools/trlc_rst:trlc_rst"), + executable = True, + allow_files = True, + cfg = "exec", + ), + "_lobster_trlc": attr.label( + default = Label("@lobster//:lobster-trlc"), + executable = True, + allow_files = True, + cfg = "exec", + doc = "lobster-trlc executable used to generate FM and CM lobster files.", + ), + "_fm_lobster_config": attr.label( + default = Label("//bazel/rules/rules_score/config:failuremodes_config"), + allow_single_file = True, + doc = "lobster-trlc YAML config for FailureMode records.", + ), + "_cm_lobster_config": attr.label( + default = Label("//bazel/rules/rules_score/config:controlmeasures_config"), + allow_single_file = True, + doc = "lobster-trlc YAML config for ControlMeasure records.", + ), + "_template": attr.label( + default = Label("//bazel/rules/rules_score:templates/fmea.template.rst"), + allow_single_file = True, + doc = "RST template for the FMEA page.", ), }, ) @@ -120,26 +374,31 @@ def fmea( controlmeasures = [], root_causes = [], arch_design = None, - visibility = None): - """Define FMEA documentation following S-CORE process guidelines. + visibility = None, + tags = []): + """Define FMEA (Failure Mode and Effects Analysis) following S-CORE process guidelines. + + Generates a single ``fmea.rst`` page with up to three sections: + Failure Modes (TRLC), Control Measures (TRLC), and optionally a + Root Causes section with FTA PlantUML diagrams. + + FTA diagrams passed via ``root_causes`` are preprocessed to inline + ``fta_metamodel.puml`` (hermetic, no ``!include`` at render time) and + lobster traceability items are extracted to ``fta.lobster``. - FMEA (Failure Mode and Effects Analysis) documents the failure modes of a - component, the control measures that mitigate them, and optional fault tree - analysis diagrams that trace root causes. + This is a **build-only** rule. The combined traceability test + (FM + CM + FTA) is owned by the ``dependability_analysis`` that wraps + this target. Args: - name: The name of the FMEA target. - failuremodes: Optional list of labels to documentation files or - requirements targets containing identified failure modes. - controlmeasures: Optional list of labels to documentation files or - requirements targets containing control measures that mitigate - identified failure modes. Can reference Assumptions of Use or - requirements as defined in the S-CORE process. - root_causes: Optional list of labels to Fault Tree Analysis diagram - files (.puml, .plantuml, .png, .svg) tracing root causes. - arch_design: Optional label to an architectural_design target for - establishing traceability between the FMEA and the architecture. - visibility: Bazel visibility specification. Default is None. + name: Target name. + failuremodes: trlc_requirements targets for failure mode records. + controlmeasures: trlc_requirements targets for control measure records. + root_causes: Optional FTA PlantUML diagram files (``.puml`` / + ``.plantuml``) representing the root causes of failure modes. + arch_design: Optional architectural_design target for traceability. + visibility: Bazel visibility. + tags: Additional Bazel tags. """ _fmea( name = name, @@ -148,4 +407,5 @@ def fmea( root_causes = root_causes, arch_design = arch_design, visibility = visibility, + tags = tags, ) diff --git a/bazel/rules/rules_score/private/lobster_config.bzl b/bazel/rules/rules_score/private/lobster_config.bzl new file mode 100644 index 00000000..6a1924fb --- /dev/null +++ b/bazel/rules/rules_score/private/lobster_config.bzl @@ -0,0 +1,33 @@ +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* + +""" +Utilities for filling out Lobster traceability report config templates. + +Report configs are static .tpl files in tools/lobster/templates/; the only +dynamic part is the list of source file paths for each level. Rules call +format_lobster_sources() to build the source-line block, then pass it as a +substitution value to ctx.actions.expand_template(). +""" + +def format_lobster_sources(files): + """Format a list of File objects as lobster source lines. + + Args: + files: List of File objects to include as sources. + + Returns: + String containing one `` source: "...";`` line per file, suitable + for substituting into a lobster config template placeholder. + """ + return "\n".join([' source: "{}";'.format(f.path) for f in files]) diff --git a/bazel/rules/rules_score/private/requirements.bzl b/bazel/rules/rules_score/private/requirements.bzl new file mode 100644 index 00000000..b4579d91 --- /dev/null +++ b/bazel/rules/rules_score/private/requirements.bzl @@ -0,0 +1,171 @@ +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* + +""" +Requirements build rules for S-CORE projects. + +This module provides macros and rules for defining requirements at any level +(feature, component, etc.) following S-CORE process guidelines. +""" + +load("@lobster//:lobster.bzl", "subrule_lobster_trlc") +load("@trlc//:trlc.bzl", "TrlcProviderInfo", "trlc_requirements_test") +load("//bazel/rules/rules_score:providers.bzl", "ComponentRequirementsInfo", "FeatureRequirementsInfo", "SphinxSourcesInfo") + +# ============================================================================ +# Private Rule Implementation +# ============================================================================ + +def _requirements_impl(ctx): + """Implementation for requirements rule. + + Collects requirement source files, renders TRLC to RST, + and extracts lobster traceability items. + + Args: + ctx: Rule context + + Returns: + List of providers including DefaultInfo, FeatureRequirementsInfo or ComponentRequirementsInfo, + and SphinxSourcesInfo + """ + rendered_files = [] + + for src in ctx.attr.srcs: + trlc_provider = src[TrlcProviderInfo] + rendered_file = ctx.actions.declare_file("{}_{}.rst".format(ctx.attr.name, src.label.name)) + + args = ctx.actions.args() + args.add("--output", rendered_file.path) + args.add("--input-dir", ".") + args.add("--source-files") + args.add_all(trlc_provider.reqs) + + ctx.actions.run( + inputs = src[DefaultInfo].files, + outputs = [rendered_file], + arguments = [args], + executable = ctx.executable._renderer, + ) + rendered_files.append(rendered_file) + + all_srcs = depset(rendered_files) + + lobster_trlc_file, _lobster_trlc = subrule_lobster_trlc(ctx.files.srcs, ctx.file.lobster_config) + + if ctx.attr.req_kind == "feature": + req_provider = FeatureRequirementsInfo( + srcs = depset([lobster_trlc_file]), + name = ctx.label.name, + ) + else: + req_provider = ComponentRequirementsInfo( + srcs = depset([lobster_trlc_file]), + name = ctx.label.name, + ) + + return [ + DefaultInfo(files = all_srcs), + req_provider, + SphinxSourcesInfo( + srcs = all_srcs, + deps = all_srcs, + ), + ] + +# ============================================================================ +# Rule Definition +# ============================================================================ + +_requirements = rule( + implementation = _requirements_impl, + doc = "Collects requirements documents for S-CORE process compliance", + attrs = { + "srcs": attr.label_list( + providers = [TrlcProviderInfo], + mandatory = True, + doc = "TRLC requirement targets providing TrlcProviderInfo", + ), + "lobster_config": attr.label( + allow_single_file = True, + mandatory = True, + doc = "Lobster YAML configuration file for traceability extraction", + ), + "req_kind": attr.string( + values = ["feature", "component"], + mandatory = True, + doc = "Kind of requirements: 'feature' or 'component'", + ), + "_renderer": attr.label( + default = Label("@trlc//tools/trlc_rst:trlc_rst"), + executable = True, + allow_files = True, + cfg = "exec", + ), + }, + subrules = [subrule_lobster_trlc], +) + +# ============================================================================ +# Public Macros +# ============================================================================ + +def feature_requirements( + name, + srcs, + visibility = None): + """Define feature requirements following S-CORE process guidelines. + + Args: + name: The name of the target. + srcs: List of labels to trlc_requirements targets providing TrlcProviderInfo. + visibility: Bazel visibility specification. + """ + _requirements( + name = name, + srcs = srcs, + lobster_config = "//bazel/rules/rules_score/config:feature_requirement", + req_kind = "feature", + visibility = visibility, + ) + + trlc_requirements_test( + name = name + "_test", + reqs = srcs, + visibility = visibility, + ) + +def component_requirements( + name, + srcs = [], + visibility = None): + """Define component requirements following S-CORE process guidelines. + + Args: + name: The name of the target. + srcs: List of labels to trlc_requirements targets providing TrlcProviderInfo. + visibility: Bazel visibility specification. + """ + _requirements( + name = name, + srcs = srcs, + lobster_config = "//bazel/rules/rules_score/config:component_requirement", + req_kind = "component", + visibility = visibility, + ) + + trlc_requirements_test( + name = name + "_test", + reqs = srcs, + visibility = visibility, + ) diff --git a/bazel/rules/rules_score/private/sphinx_module.bzl b/bazel/rules/rules_score/private/sphinx_module.bzl index 2ac80f22..d4baa109 100644 --- a/bazel/rules/rules_score/private/sphinx_module.bzl +++ b/bazel/rules/rules_score/private/sphinx_module.bzl @@ -1,23 +1,5 @@ -# ******************************************************************************* -# 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 -# ******************************************************************************* -# ====================================================================================== -# Providers -# ====================================================================================== - load("//bazel/rules/rules_score:providers.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") -# SphinxModuleInfo and SphinxNeedsInfo are re-exported from providers.bzl for backward compatibility. - # ====================================================================================== # Helpers # ====================================================================================== @@ -125,7 +107,6 @@ def _score_html_impl(ctx): needs_external_needs[dep.label.name] = { "base_url": dep_name, # Relative path to the subdirectory where dep HTML is copied "json_path": dep[SphinxNeedsInfo].needs_json_file.path, # Use direct file - "version": "1.0", "id_prefix": "", "css_class": "", } @@ -141,7 +122,17 @@ def _score_html_impl(ctx): content = json.encode_indent(needs_external_needs, indent = " "), ) - config_file = _create_config_py(ctx) + # Read template and substitute PROJECT_NAME + config_file = ctx.actions.declare_file(ctx.label.name + "/conf.py") + template = sphinx_toolchain.conf_template.files.to_list()[0] + + ctx.actions.expand_template( + template = template, + output = config_file, + substitutions = { + "{PROJECT_NAME}": ctx.label.name.replace("_", " ").title(), + }, + ) # Build HTML with external needs html_inputs = ctx.files.srcs + ctx.files.needs + [config_file, needs_external_needs_json] @@ -243,13 +234,11 @@ def sphinx_module( transitive dependency collection. All dependencies are automatically included in a modules/ subdirectory for intersphinx cross-referencing. - The Sphinx binary and conf.py template are provided by the registered - sphinx_toolchain. See sphinx_toolchain.bzl for configuration details. - Args: name: Name of the target srcs: List of source files (.rst, .md) with index file first index: Label to index.rst file + config: Label to conf.py configuration file (optional, will be auto-generated if not provided) deps: List of other sphinx_module targets this module depends on sphinx: Label to sphinx build binary (default: :sphinx_build) visibility: Bazel visibility diff --git a/bazel/rules/rules_score/private/unit.bzl b/bazel/rules/rules_score/private/unit.bzl index 835b3a1a..c03e2b9d 100644 --- a/bazel/rules/rules_score/private/unit.bzl +++ b/bazel/rules/rules_score/private/unit.bzl @@ -19,7 +19,11 @@ following S-CORE process guidelines. A unit is the smallest testable software element with associated design, implementation, and tests. """ -load("//bazel/rules/rules_score:providers.bzl", "SphinxSourcesInfo", "UnitInfo") +load("@lobster//:lobster.bzl", "subrule_gtest_report") +load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") +load("@rules_rust//rust:defs.bzl", "rust_common") +load("//bazel/rules/rules_score:providers.bzl", "CcDependencyInfo", "CertifiedScope", "SphinxSourcesInfo", "UnitDesignInfo", "UnitInfo") +load(":cc_dependency_aspect.bzl", "cc_dependencies_aspect") # ============================================================================ # Private Rule Implementation @@ -40,33 +44,58 @@ def _unit_impl(ctx): # Collect design files from unit_design targets design_files = [] + sphinx_design_deps = [] + design_static_fbs = [] + design_dynamic_fbs = [] for design_target in ctx.attr.unit_design: if SphinxSourcesInfo in design_target: design_files.append(design_target[SphinxSourcesInfo].srcs) + sphinx_design_deps.append(design_target[SphinxSourcesInfo].deps) + if UnitDesignInfo in design_target: + design_static_fbs.append(design_target[UnitDesignInfo].static) + design_dynamic_fbs.append(design_target[UnitDesignInfo].dynamic) design_depset = depset(transitive = design_files) + design_static_fbs_depset = depset(transitive = design_static_fbs) + design_dynamic_fbs_depset = depset(transitive = design_dynamic_fbs) - # Collect implementation and test targets - # Include scope targets in the implementation depset - implementation_depset = depset(ctx.attr.implementation + ctx.attr.scope) - tests_depset = depset(ctx.attr.tests) + # Run each test executable via subrule_gtest_report and collect the XML outputs + xml_files = [] + for test_target in ctx.attr.tests: + pkg = test_target.label.package.replace("/", "_") + test_name = test_target.label.name.replace("/", "_") + unique_name = "{}_{}_{}_gtest_report".format(ctx.label.name, pkg, test_name) + xml = subrule_gtest_report(unique_name, test_target.files_to_run.executable, test_target.default_runfiles.files) + xml_files.append(xml) + + tests_depset = depset(xml_files) # Combine all files for DefaultInfo all_files = depset( + xml_files, transitive = [design_depset], ) + collected_dependent_labels = [] + for impl in ctx.attr.implementation: + if CcDependencyInfo in impl: + collected_dependent_labels.append(impl[CcDependencyInfo].dependencies) + return [ DefaultInfo(files = all_files), + CertifiedScope(transitive_scopes = depset(ctx.attr.scope)), UnitInfo( name = ctx.label.name, unit_design = design_depset, - implementation = implementation_depset, + unit_design_static_fbs = design_static_fbs_depset, + unit_design_dynamic_fbs = design_dynamic_fbs_depset, + implementation = depset(ctx.attr.implementation), tests = tests_depset, + dependent_labels = depset(transitive = collected_dependent_labels), ), SphinxSourcesInfo( srcs = all_files, - transitive_srcs = all_files, + deps = depset(transitive = [all_files] + sphinx_design_deps), ), ] @@ -77,21 +106,26 @@ def _unit_impl(ctx): _unit = rule( implementation = _unit_impl, doc = "Defines a software unit with design, implementation, and tests for S-CORE process compliance", + subrules = [subrule_gtest_report], attrs = { "unit_design": attr.label_list( mandatory = True, - doc = "Unit design artifacts (typically architectural_design targets)", + providers = [UnitDesignInfo], + doc = "Unit design artifacts (unit_design targets only)", ), "implementation": attr.label_list( mandatory = True, - doc = "Implementation targets (cc_library, py_library, rust_library, etc.)", + providers = [[CcInfo], [rust_common.crate_info]], + aspects = [cc_dependencies_aspect], + doc = "Implementation targets (cc_library, cc_binary, rust_library, rust_binary, etc.)", ), - "scope": attr.label_list( + "scope": attr.string_list( default = [], doc = "Additional not explicitly named targets which are needed for the unit implementation", ), "tests": attr.label_list( mandatory = True, + cfg = "exec", doc = "Test targets that verify the unit (cc_test, py_test, rust_test, etc.)", ), }, @@ -119,13 +153,13 @@ def unit( Args: name: The name of the unit. Used as the target name. - unit_design: List of labels to architectural_design targets or design - documentation that describes the unit's internal structure and behavior. + unit_design: List of labels to unit_design targets that describe the + unit's internal structure and behavior. implementation: List of labels to Bazel targets representing the actual - implementation (cc_library, py_library, rust_library, etc.). + implementation (cc_library, cc_binary, rust_library, rust_binary, etc.). scope: Optional list of additional targets needed for the unit implementation but not explicitly named in the implementation list. Default is empty list. - tests: List of labels to Bazel test targets (cc_test, py_test, rust_test, etc.) + tests: List of labels to Bazel test targets (cc_test, rust_test, etc.) that verify the unit implementation. testonly: If true, only testonly targets can depend on this unit. Set to true when the unit depends on testonly targets like tests. @@ -135,7 +169,7 @@ def unit( ```python unit( name = "kvs_unit1", - unit_design = [":kvs_architectural_design"], + unit_design = [":kvs_unit_design"], implementation = [ "//persistency/kvs:lib1", "//persistency/kvs:lib2", diff --git a/bazel/rules/rules_score/private/unit_design.bzl b/bazel/rules/rules_score/private/unit_design.bzl index 6724baa9..125fcb4f 100644 --- a/bazel/rules/rules_score/private/unit_design.bzl +++ b/bazel/rules/rules_score/private/unit_design.bzl @@ -16,7 +16,12 @@ Unit Design build rules for S-CORE projects. This module provides macros and rules for defining unit design documentation following S-CORE process guidelines. Unit design documents describe the internal -design of a software unit, including static and dynamic views. +design of a software unit including static and dynamic views. + +PlantUML files (.puml) are passed through to Sphinx for rendering via +the sphinxcontrib-plantuml extension. The rule also invokes the PlantUML +parser pipeline to produce FlatBuffers outputs that can be consumed by +verification tooling. """ load("//bazel/rules/rules_score:providers.bzl", "SphinxSourcesInfo", "UnitDesignInfo") @@ -25,11 +30,43 @@ load("//bazel/rules/rules_score:providers.bzl", "SphinxSourcesInfo", "UnitDesign # Private Rule Implementation # ============================================================================ +def _run_puml_parser(ctx, puml_file): + """Run the PlantUML parser on one .puml file and emit a FlatBuffers file.""" + file_stem = puml_file.basename.rsplit(".", 1)[0] + fbs_output = ctx.actions.declare_file( + "{}/{}.fbs.bin".format(ctx.label.name, file_stem), + ) + + ctx.actions.run( + inputs = [puml_file], + outputs = [fbs_output], + executable = ctx.executable._puml_parser, + arguments = [ + "--file", + puml_file.path, + "--fbs-output-dir", + fbs_output.dirname, + ], + progress_message = "Parsing Unit Design PlantUML diagram: %s" % puml_file.short_path, + ) + + return fbs_output + +def _parse_puml_diagrams(ctx, files): + """Run parser on all .puml/.plantuml files from a list and return fbs outputs.""" + fbs_outputs = [] + for f in files: + if f.extension in ("puml", "plantuml"): + fbs_outputs.append(_run_puml_parser(ctx, f)) + return fbs_outputs + def _unit_design_impl(ctx): """Implementation for unit_design rule. - Collects unit design artifacts (RST documents and diagram files) and + Collects unit design artifacts (RST documents and PlantUML diagrams) and provides them through the UnitDesignInfo and SphinxSourcesInfo providers. + PlantUML files are passed through for Sphinx rendering and parsed into + FlatBuffers binaries. Args: ctx: Rule context @@ -37,15 +74,13 @@ def _unit_design_impl(ctx): Returns: List of providers including DefaultInfo, UnitDesignInfo, SphinxSourcesInfo """ + all_source_files = depset( transitive = [depset(ctx.files.static), depset(ctx.files.dynamic)], ) - # TODO: invoke diagram parser here to produce structured design artifacts - # for documentation and traceability (e.g. generate parsed binary - # representations of the diagrams for downstream analysis tools). - static_fbs = depset() - dynamic_fbs = depset() + static_fbs = depset(_parse_puml_diagrams(ctx, ctx.files.static)) + dynamic_fbs = depset(_parse_puml_diagrams(ctx, ctx.files.dynamic)) return [ DefaultInfo(files = all_source_files), @@ -56,7 +91,7 @@ def _unit_design_impl(ctx): ), SphinxSourcesInfo( srcs = all_source_files, - transitive_srcs = all_source_files, + deps = all_source_files, ), ] @@ -66,15 +101,24 @@ def _unit_design_impl(ctx): _unit_design = rule( implementation = _unit_design_impl, - doc = "Collects unit design documents and diagrams for S-CORE process compliance.", + doc = "Collects unit design documents and diagrams for S-CORE process compliance. " + + "PlantUML files are passed through to Sphinx and parsed to FlatBuffers.", attrs = { "static": attr.label_list( allow_files = [".puml", ".plantuml", ".svg", ".rst", ".md"], - doc = "Static design views (e.g., class diagrams, state machine diagrams).", + mandatory = False, + doc = "Static unit design diagrams (class diagrams, etc.)", ), "dynamic": attr.label_list( allow_files = [".puml", ".plantuml", ".svg", ".rst", ".md"], - doc = "Dynamic design views (e.g., sequence diagrams, activity diagrams).", + mandatory = False, + doc = "Dynamic unit design diagrams (sequence diagrams, etc.)", + ), + "_puml_parser": attr.label( + default = Label("//plantuml/parser:parser"), + executable = True, + cfg = "exec", + doc = "PlantUML parser tool that generates FlatBuffers from .puml files", ), }, ) @@ -88,20 +132,55 @@ def unit_design( static = [], dynamic = [], visibility = None): - """Define unit design documentation following S-CORE process guidelines. + """Define unit design following S-CORE process guidelines. - A unit design describes the internal design of a software unit. It consists - of static views (e.g., class diagrams) and dynamic views (e.g., sequence - diagrams). + Unit design documents describe the internal design of a software unit, + including both static and dynamic views. Static views show the structural + organization (classes, data types), while dynamic views show the behavioral + aspects (sequences, state transitions). + + Include the PlantUML diagram file (``.puml``) together with an RST wrapper + that references it via ``.. uml::`` so that Sphinx renders the diagram. + Unit design diagrams are also parsed into FlatBuffers during the build. Args: name: The name of the unit design target. - static: List of labels to static design view files (.puml, .plantuml, - .svg, .rst, .md). Default is empty list. - dynamic: List of labels to dynamic design view files (.puml, .plantuml, - .svg, .rst, .md). Default is empty list. - visibility: Bazel visibility specification. Default is None. + static: Optional list of labels to diagram files (.puml, .plantuml, + .svg) or documentation files (.rst, .md) containing static + design views such as class diagrams. Include both the RST wrapper + and the referenced .puml file(s) together. + dynamic: Optional list of labels to diagram files (.puml, .plantuml, + .svg) or documentation files (.rst, .md) containing dynamic + design views such as sequence diagrams. + visibility: Bazel visibility specification for the generated targets. + + RST Fragment Convention: + Design RST files are inlined into the generated unit page via + ``.. include::``. Because the parent document already uses ``=`` + (section title) and ``-`` (subsection), any title inside the design + RST **must** use a heading character that has not yet appeared — such + as ``^`` — so it becomes a sub-subsection rather than a top-level + section:: + + My Diagram Title + ^^^^^^^^^^^^^^^^ + + .. uml:: my_diagram.puml + + Example: + ```starlark + unit_design( + name = "my_unit_design", + static = [ + "class_diagram.rst", # RST fragment (use ^ for headings) + ], + dynamic = [ + "sequence_diagram.puml", + ], + ) + ``` """ + _unit_design( name = name, static = static, diff --git a/bazel/rules/rules_score/providers.bzl b/bazel/rules/rules_score/providers.bzl index 9f352719..f0e63f08 100644 --- a/bazel/rules/rules_score/providers.bzl +++ b/bazel/rules/rules_score/providers.bzl @@ -22,6 +22,26 @@ build rules to enable consistent Sphinx documentation generation. # Provider Definitions # ============================================================================ +CertifiedScope = provider( + doc = """Holds the scope labels that are certified by this target. + + This provider aggregates the list of labels which are under the + certification scope of this target. + + Valid values are either: + * an existing label + * a package (e.g. //some/package:__pkg__) + * a package and its subpackages (e.g. //some/package:__subpackages__) + + This follows the Bazel visibility patterns. + Note that broad visibility labels (//visibility:public or //visibility:private) + do not make sense for this kind of scoping and are thus not functional. + """, + fields = { + "transitive_scopes": "Depset of Labels (packages or targets) that are under the certification scope of this target, collected transitively.", + }, +) + SphinxSourcesInfo = provider( doc = """Provider for Sphinx documentation source files. @@ -31,98 +51,143 @@ SphinxSourcesInfo = provider( this to enable integration with sphinx_module and dependable_element. """, fields = { - "srcs": "Depset of source files for Sphinx documentation (.rst, .md, .puml, .plantuml, .svg, .png, etc.)", - "transitive_srcs": "Depset of transitive source files from dependencies", + "srcs": "Depset of direct source files for Sphinx documentation (.rst, .md, .puml, .plantuml, .svg, .png, etc.)", + "deps": "Depset of transitive Sphinx source files collected from all direct and transitive dependencies.", }, ) UnitInfo = provider( - doc = "Provider for unit artifacts", + doc = "Provider for unit artifacts.", fields = { - "name": "Name of the unit target", - "unit_design": "Depset of unit design artifacts (architectural design)", - "implementation": "Depset of implementation targets (libraries, binaries)", - "tests": "Depset of test targets", + "name": "Name of the unit target.", + "unit_design": "Depset of design artifact files (PlantUML diagrams, RST documents, etc.).", + "unit_design_static_fbs": "Depset of FlatBuffers binaries generated from static unit_design diagrams.", + "unit_design_dynamic_fbs": "Depset of FlatBuffers binaries generated from dynamic unit_design diagrams.", + "implementation": "Depset of implementation targets (cc_library, rust_library, etc.).", + "tests": "Depset of test targets (cc_test, rust_test, etc.).", + "dependent_labels": "Depset of Labels that this unit's implementation depends on transitively (used for certification scope validation).", }, ) -ComponentInfo = provider( - doc = "Provider for component artifacts", +FeatureRequirementsInfo = provider( + doc = "Provider for feature requirements artifacts.", fields = { - "name": "Name of the component target", - "requirements": "Depset of component requirements artifacts", - "components": "Depset of unit targets that comprise this component", - "tests": "Depset of component-level integration test targets", + "srcs": "Depset of .lobster traceability files generated from TRLC requirement sources.", + "name": "Name of the requirements target.", }, ) -UnitDesignInfo = provider( - doc = """Provider for unit design artifacts. +ComponentRequirementsInfo = provider( + doc = "Provider for component requirements artifacts.", + fields = { + "srcs": "Depset of .lobster traceability files generated from TRLC requirement sources.", + "name": "Name of the requirements target.", + }, +) - Carries parsed representations of static and dynamic design views for a - software unit. The depset fields are populated once a diagram parser is - integrated; until then they are empty stubs. - """, +AnalysisInfo = provider( + doc = "Provider for safety analysis traceability artifacts (lobster files).", fields = { - "static": "Depset of parsed static design view artifacts (e.g., class diagrams)", - "dynamic": "Depset of parsed dynamic design view artifacts (e.g., sequence diagrams)", - "name": "Name of the unit design target", + "name": "Name of the analysis target.", + "lobster_files": "Dict mapping canonical lobster file names to File objects " + + "(e.g. {'failuremodes.lobster': File, 'root_causes.lobster': File}).", }, ) -ArchitecturalDesignInfo = provider( - doc = "Provider for architectural design artifacts", +AssumptionsOfUseInfo = provider( + doc = "Provider for assumptions of use artifacts.", fields = { - "static": "Depset of static architecture diagram files (e.g., class diagrams, component diagrams)", - "dynamic": "Depset of dynamic architecture diagram files (e.g., sequence diagrams, activity diagrams)", - "name": "Name of the architectural design target", + "srcs": "Depset of .lobster traceability files collected from all linked requirements targets.", + "requirements": "List of FeatureRequirementsInfo or ComponentRequirementsInfo providers this AoU traces to.", + "name": "Name of the assumptions of use target.", }, ) -AnalysisInfo = provider( - doc = "Provider for FMEA and safety analysis artifacts", +ComponentInfo = provider( + doc = "Provider for component artifacts.", fields = { - "controlmeasures": "Depset of control measures documentation or requirements", - "failuremodes": "Depset of failure modes documentation or requirements", - "fta": "Depset of Fault Tree Analysis diagrams", - "arch_design": "ArchitecturalDesignInfo provider for linked architectural design", - "name": "Name of the analysis target", + "name": "Name of the component target.", + "requirements": "Depset of requirement traceability files (.lobster) collected from requirements targets, including transitive files from nested components.", + "components": "Depset of nested component and/or unit Targets that comprise this component.", + "tests": "Depset of test traceability files (.lobster) generated from unit test results, collected transitively from all nested components and units.", + "architecture": "Depset of architecture traceability files (.lobster) generated from unit architectural designs, collected transitively from all nested components and units.", + "dependent_labels": "Depset of Labels that this component's implementation depends on transitively (collected from all nested units and components, used for certification scope validation).", }, ) -DependabilityAnalysisInfo = provider( - doc = "Provider for dependability analysis artifacts", +CcDependencyInfo = provider( + doc = """Provider for collecting transitive dependencies from C/C++ targets. + + This provider aggregates all Labels that a cc_library or cc_binary + target depends on transitively. + """, fields = { - "safety_analysis": "List of AnalysisInfo providers", - "security_analysis": "List of AnalysisInfo providers", - "arch_design": "ArchitecturalDesignInfo provider for linked architectural design", - "name": "Name of the dependability analysis target", + "dependencies": "Depset of Labels representing all transitive C/C++ dependencies of a target.", }, ) -FeatureRequirementsInfo = provider( - doc = "Provider for feature requirements artifacts", +DependableElementInfo = provider( + doc = """Provider for dependable element metadata. + + Carries the integrity level of a dependable element so that consumers + (e.g. other dependable elements that list this one in their `deps`) + can perform integrity-level compatibility checks. + + Allowed values for `integrity_level` are "A", "B", "C", "D" where + D > C > B > A (D being the highest / most stringent). + """, fields = { - "srcs": "Depset of source files containing feature requirements", - "name": "Name of the feature requirements target", + "integrity_level": "String – one of 'A', 'B', 'C', 'D'.", + "label": "Label of the dependable element that produced this provider.", }, ) -ComponentRequirementsInfo = provider( - doc = "Provider for component requirements artifacts", +ArchitecturalDesignInfo = provider( + doc = "Provider for architectural design artifacts including parsed design metadata.", fields = { - "srcs": "Depset of source files containing component requirements", - "requirements": "List of FeatureRequirementsInfo providers this component traces to", - "name": "Name of the component requirements target", + "static": "Depset of FlatBuffers binaries for static architecture diagrams (class diagrams, component diagrams, etc.)", + "dynamic": "Depset of FlatBuffers binaries for dynamic architecture diagrams (sequence diagrams, activity diagrams, etc.)", + "name": "Name of the architectural design target", + "lobster_files": "Depset of .lobster traceability files generated by the PlantUML parser from component diagrams.", + "public_api_lobster_files": "Depset of .lobster traceability files generated from public_api diagrams (subset of lobster_files).", }, ) -AssumptionsOfUseInfo = provider( - doc = "Provider for assumptions of use artifacts", +UnitDesignInfo = provider( + doc = "Provider for unit design artifacts including parsed design metadata.", + fields = { + "static": "Depset of FlatBuffers binaries for static unit design diagrams (class diagrams, etc.)", + "dynamic": "Depset of FlatBuffers binaries for dynamic unit design diagrams (sequence diagrams, etc.)", + "name": "Name of the unit design target", + }, +) + +DependabilityAnalysisInfo = provider( + doc = """Provider for dependability analysis artifacts. + + Aggregates sub-analyses: + * **fmea** – fmea rule targets (FM + CM + optional root causes). + * **security_analysis** – security analysis targets (placeholder). + """, + fields = { + "fmea": "Depset of output files from fmea targets.", + "security_analysis": "Depset of output files from security analysis targets.", + "dfa": "Depset of DFA documentation files (placeholder).", + "arch_design": "ArchitecturalDesignInfo from the linked architectural design (placeholder).", + "name": "Name of the dependability analysis target.", + "lobster_files": "Dict mapping canonical lobster file names to File objects collected from sub-analyses only (FM, CM, RC). Does not include architecture lobster files; those are obtained separately via ArchitecturalDesignInfo.lobster_files.", + }, +) + +DependableElementLobsterInfo = provider( + doc = """Provider carrying the lobster traceability report produced by a dependable element. + + Exposed so the main dependable_element test rule can pick up the already-built + report and wire it into the test executable without running lobster a second time. + """, fields = { - "srcs": "Depset of source files containing assumptions of use", - "feature_requirements": "List of FeatureRequirementsInfo providers this AoU traces to", - "name": "Name of the assumptions of use target", + "lobster_report": "The lobster report JSON File object, or None when no traceability data is available.", + "lobster_html_report": "The lobster HTML report File object, or None when no traceability data is available.", }, ) diff --git a/bazel/rules/rules_score/rules_score.bzl b/bazel/rules/rules_score/rules_score.bzl index 27b983a6..8296c9f7 100644 --- a/bazel/rules/rules_score/rules_score.bzl +++ b/bazel/rules/rules_score/rules_score.bzl @@ -1,16 +1,3 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* - load( "//bazel/rules/rules_score:providers.bzl", _ComponentInfo = "ComponentInfo", @@ -29,10 +16,6 @@ load( "//bazel/rules/rules_score/private:component.bzl", _component = "component", ) -load( - "//bazel/rules/rules_score/private:component_requirements.bzl", - _component_requirements = "component_requirements", -) load( "//bazel/rules/rules_score/private:dependability_analysis.bzl", _dependability_analysis = "dependability_analysis", @@ -41,14 +24,15 @@ load( "//bazel/rules/rules_score/private:dependable_element.bzl", _dependable_element = "dependable_element", ) -load( - "//bazel/rules/rules_score/private:feature_requirements.bzl", - _feature_requirements = "feature_requirements", -) load( "//bazel/rules/rules_score/private:fmea.bzl", _fmea = "fmea", ) +load( + "//bazel/rules/rules_score/private:requirements.bzl", + _component_requirements = "component_requirements", + _feature_requirements = "feature_requirements", +) load( "//bazel/rules/rules_score/private:sphinx_module.bzl", _sphinx_module = "sphinx_module", diff --git a/bazel/rules/rules_score/sphinx_toolchain.bzl b/bazel/rules/rules_score/sphinx_toolchain.bzl index b1747236..dfb2d20e 100644 --- a/bazel/rules/rules_score/sphinx_toolchain.bzl +++ b/bazel/rules/rules_score/sphinx_toolchain.bzl @@ -1,16 +1,3 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* - SphinxInfo = provider( doc = "Provider for Sphinx Toolchain", fields = { @@ -34,14 +21,14 @@ sphinx_toolchain = rule( implementation = _sphinx_toolchain_impl, attrs = { "sphinx": attr.label( - default = "@score_tooling//bazel/rules/rules_score:raw_build", + default = Label("//bazel/rules/rules_score:raw_build"), ), "conf_template": attr.label( allow_single_file = True, - default = "@score_tooling//bazel/rules/rules_score:templates/conf.template.py", + default = Label("//bazel/rules/rules_score:templates/conf.template.py"), ), "html_merge_tool": attr.label( - default = "@score_tooling//bazel/rules/rules_score:sphinx_html_merge", + default = Label("//bazel/rules/rules_score:sphinx_html_merge"), ), }, ) diff --git a/bazel/rules/rules_score/src/arch_to_reqs_from_lobster.py b/bazel/rules/rules_score/src/arch_to_reqs_from_lobster.py new file mode 100644 index 00000000..73cca322 --- /dev/null +++ b/bazel/rules/rules_score/src/arch_to_reqs_from_lobster.py @@ -0,0 +1,146 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +"""Generate a lobster architecture item from component requirements. + +Extracts requirement tags from component requirement .lobster files and +produces an architecture.lobster file with a single item representing the +component. The item references all component requirements allocated to this +component through Bazel (via TRLC). + +Usage: + python arch_to_reqs_from_lobster.py \\ + --component-name my_component \ + --req-lobster comp_reqs_1.lobster comp_reqs_2.lobster \ + --output architecture.lobster +""" + +import argparse +import json +from pathlib import Path + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Generate architecture lobster item for a component" + ) + parser.add_argument( + "--component-name", + required=True, + help="Name of the component Bazel target (e.g. my_component)", + ) + parser.add_argument( + "--req-lobster", + nargs="+", + required=True, + help="Component requirement .lobster files to extract requirement tags from", + ) + parser.add_argument( + "--build-file", + required=True, + help="Workspace-relative path to the BUILD file defining this component (e.g. examples/seooc/BUILD)", + ) + parser.add_argument( + "--output", + required=True, + help="Path for the generated architecture .lobster file", + ) + return parser.parse_args() + + +def extract_requirement_tags(lobster_files: list[str]) -> list[str]: + """Extract all requirement tags from .lobster files. + + Args: + lobster_files: Paths to .lobster JSON files with lobster-req-trace schema. + + Returns: + Sorted list of unique requirement tag strings + (e.g. "req SampleComponent.REQ_COMP_001"). + """ + tags: set[str] = set() + for path in lobster_files: + try: + with open(path, encoding="utf-8") as f: + data = json.load(f) + except (json.JSONDecodeError, OSError) as e: + raise SystemExit(f"Failed to parse {path}: {e}") from e + for item in data.get("data", []): + tag = item.get("tag") + if tag: + tags.add(tag) + return sorted(tags) + + +def build_architecture_lobster( + component_name: str, build_file: str, requirement_tags: list[str] +) -> dict: + """Build the architecture lobster-imp-trace structure. + + Creates a single lobster implementation item representing the component. + The item references all component requirement tags, reflecting the + Bazel-level structural allocation. + + Args: + component_name: Workspace-qualified label of the component (e.g. //pkg:name). + build_file: Workspace-relative path to the BUILD file (e.g. examples/seooc/BUILD). + requirement_tags: Requirement tag strings to reference. + + Returns: + Dict representing the complete lobster-imp-trace JSON structure. + """ + return { + "data": [ + { + "tag": f"arch {component_name}", + "location": { + "kind": "file", + "file": build_file, + "line": 1, + "column": None, + }, + "name": component_name, + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "refs": requirement_tags, + "language": "Bazel", + "kind": "Architecture", + } + ], + "generator": "score_arch_to_lobster", + "schema": "lobster-imp-trace", + "version": 3, + } + + +def main() -> None: + args = parse_args() + req_tags = extract_requirement_tags(args.req_lobster) + if not req_tags: + message = ( + f"error: no requirement tags found in {args.req_lobster}; " + "cannot generate an Architecture item with no requirement links" + ) + raise SystemExit(message) + arch_lobster = build_architecture_lobster( + args.component_name, args.build_file, req_tags + ) + Path(args.output).write_text( + json.dumps(arch_lobster, indent=2) + "\n", encoding="utf-8" + ) + + +if __name__ == "__main__": + main() diff --git a/bazel/rules/rules_score/src/safety_analysis_tools.py b/bazel/rules/rules_score/src/safety_analysis_tools.py new file mode 100644 index 00000000..cc8c2927 --- /dev/null +++ b/bazel/rules/rules_score/src/safety_analysis_tools.py @@ -0,0 +1,279 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +""" +FTA PlantUML lobster linker. + +Parses PlantUML FTA diagrams and extracts ``$TopEvent`` and ``$BasicEvent`` +procedure calls, producing a ``.lobster`` file in ``lobster-act-trace`` format. + +Each extracted item uses the *alias* (second argument) as its tag and name +because the alias is the TRLC fully-qualified name of the corresponding +safety-analysis record (e.g. ``SampleLibrary.SampleFailureMode``). A ``refs`` +entry pointing at ``req `` links the FTA artifact back to the +matching TRLC requirement in the traceability chain. + +Supported call patterns (single-line, double-quoted args):: + + $TopEvent("Human readable name", "Namespace.RecordName") + $BasicEvent("Human readable name", "Namespace.RecordName", "GateAlias") +""" + +import argparse +import json +import logging +from pathlib import Path + +logger = logging.getLogger(__name__) + +LOBSTER_GENERATOR = "safety_analysis_tools" +LOBSTER_SCHEMA = "lobster-act-trace" +LOBSTER_VERSION = 3 + +# --------------------------------------------------------------------------- +# PlantUML call parser +# TODO: Replace with Plantuml Parser +# --------------------------------------------------------------------------- + +# Procedure names and the indices of (name_arg, alias_arg) within their arg list. +_FTA_PROCEDURES: tuple[tuple[str, str, int, int], ...] = ( + ("$TopEvent", "TopEvent", 0, 1), + ("$BasicEvent", "BasicEvent", 0, 1), +) + + +def _parse_quoted_args(line: str, proc_name: str) -> list[str] | None: + """Extract double-quoted string arguments from a PlantUML procedure call. + + Finds ``proc_name(...)`` in *line*, then collects every double-quoted token + inside the parentheses. Returns ``None`` if the procedure is not present. + """ + marker = proc_name + "(" + call_start = line.find(marker) + if call_start == -1: + return None + paren_open = call_start + len(marker) - 1 + paren_close = line.find(")", paren_open + 1) + if paren_close == -1: + return None + inside = line[paren_open + 1 : paren_close] + args: list[str] = [] + pos = 0 + while pos < len(inside): + q_open = inside.find('"', pos) + if q_open == -1: + break + q_close = inside.find('"', q_open + 1) + if q_close == -1: + break + args.append(inside[q_open + 1 : q_close]) + pos = q_close + 1 + return args if args else None + + +def _is_valid_trlc_fqn(alias: str) -> bool: + """Return True when *alias* looks like ``Package.RecordName``.""" + parts = alias.split(".") + if len(parts) != 2: + return False + return all( + part + and part[0].isalpha() + or part[0] == "_" + and all(c.isalnum() or c == "_" for c in part) + for part in parts + ) + + +# --------------------------------------------------------------------------- +# Parser +# --------------------------------------------------------------------------- + + +def extract_fta_items(puml_file: str) -> list[dict]: + """Parse a PlantUML FTA file and return lobster trace items. + + Args: + puml_file: Path to the ``.puml`` file to parse. + + Returns: + List of lobster item dicts in ``lobster-act-trace`` format. + """ + path = Path(puml_file) + try: + content = path.read_text(encoding="utf-8") + except OSError: + logger.exception("Cannot read '%s'", puml_file) + raise + + items: list[dict] = [] + + for line_number, line in enumerate(content.splitlines(), start=1): + for proc_name, kind, name_idx, alias_idx in _FTA_PROCEDURES: + call_args = _parse_quoted_args(line, proc_name) + if call_args is None or len(call_args) <= max(name_idx, alias_idx): + continue + name = call_args[name_idx] + alias = call_args[alias_idx] + if not _is_valid_trlc_fqn(alias): + logger.warning( + "%s:%d: alias %r does not look like a valid " + "TRLC fully-qualified name (expected 'Package.Record')", + puml_file, + line_number, + alias, + ) + items.append( + { + "tag": f"fta {alias}", + "location": { + "kind": "file", + "file": str(path), + "line": line_number, + "column": None, + }, + "name": alias, + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "refs": [f"req {alias}"], + "framework": "PlantUML", + "kind": kind, + } + ) + logger.debug( + "Found %s: alias=%r name=%r at line %d", + kind, + alias, + name, + line_number, + ) + break # one match per line + + if not items: + logger.warning("No FTA events found in '%s'", puml_file) + + return items + + +def create_lobster_output(items: list[dict]) -> dict: + """Wrap items in the standard lobster JSON envelope.""" + return { + "data": items, + "generator": LOBSTER_GENERATOR, + "schema": LOBSTER_SCHEMA, + "version": LOBSTER_VERSION, + } + + +# --------------------------------------------------------------------------- +# PlantUML preprocessor +# --------------------------------------------------------------------------- + + +def preprocess_puml( + input_path: str, + metamodel_path: str, + output_path: str, +) -> None: + """Inline ``!include fta_metamodel.puml`` into a PlantUML file. + + Replaces the ``!include fta_metamodel.puml`` directive with the content + of the metamodel file (stripping outer ``@startuml`` / ``@enduml`` + markers so the combined file remains a valid PlantUML diagram). + + This avoids the need to colocate the metamodel alongside the diagram at + build time and eliminates fragile shell ``cp`` actions in Bazel rules. + + Args: + input_path: Path to the source ``.puml`` file. + metamodel_path: Path to ``fta_metamodel.puml``. + output_path: Path for the preprocessed output ``.puml``. + """ + metamodel = Path(metamodel_path).read_text(encoding="utf-8") + # Strip @startuml / @enduml so they don't nest inside the host diagram. + meta_lines = [ + line + for line in metamodel.splitlines(keepends=True) + if line.strip() not in ("@startuml", "@enduml") + ] + meta_content = "".join(meta_lines) + + source = Path(input_path).read_text(encoding="utf-8") + processed = source.replace("!include fta_metamodel.puml", meta_content) + + Path(output_path).write_text(processed, encoding="utf-8") + logger.info("Preprocessed %s → %s", input_path, output_path) + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- + + +def main() -> None: + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", + ) + + parser = argparse.ArgumentParser( + description="Inline fta_metamodel.puml into FTA diagrams and extract lobster traceability.", + ) + parser.add_argument( + "--metamodel", + required=True, + help="Path to fta_metamodel.puml to inline.", + ) + parser.add_argument( + "--output-dir", + required=True, + dest="output_dir", + help="Directory for the preprocessed .puml output files.", + ) + parser.add_argument( + "--lobster", + required=True, + help="Output .lobster traceability file path.", + ) + parser.add_argument( + "inputs", + nargs="+", + help="PlantUML FTA .puml files to process.", + ) + + _run_preprocess(parser.parse_args()) + + +def _run_preprocess(args: argparse.Namespace) -> None: + """Preprocess each diagram (inline metamodel) and extract lobster items.""" + output_dir = Path(args.output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + all_items: list[dict] = [] + for puml_file in args.inputs: + preprocessed_path = output_dir / Path(puml_file).name + preprocess_puml(puml_file, args.metamodel, str(preprocessed_path)) + items = extract_fta_items(puml_file) + logger.info("Extracted %d item(s) from '%s'", len(items), puml_file) + all_items.extend(items) + + lobster_output = create_lobster_output(all_items) + with open(args.lobster, "w", encoding="utf-8") as fh: + json.dump(lobster_output, fh, indent=2) + fh.write("\n") + logger.info("Wrote %d lobster item(s) to %s", len(all_items), args.lobster) + + +if __name__ == "__main__": + main() diff --git a/bazel/rules/rules_score/src/sphinx_html_merge.py b/bazel/rules/rules_score/src/sphinx_html_merge.py index 730310b1..60dfaa43 100755 --- a/bazel/rules/rules_score/src/sphinx_html_merge.py +++ b/bazel/rules/rules_score/src/sphinx_html_merge.py @@ -1,16 +1,4 @@ #!/usr/bin/env python3 -# ******************************************************************************* -# 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 -# ******************************************************************************* """Merge multiple Sphinx HTML output directories. This script merges Sphinx HTML documentation from multiple modules into a single diff --git a/bazel/rules/rules_score/src/sphinx_module_ext.py b/bazel/rules/rules_score/src/sphinx_module_ext.py new file mode 100644 index 00000000..a0301b25 --- /dev/null +++ b/bazel/rules/rules_score/src/sphinx_module_ext.py @@ -0,0 +1,136 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +# +import json +import logging +import os +import sys +from pathlib import Path +from typing import Any, Dict, List + +# Create a logger with the Sphinx namespace +logger = logging.getLogger(__name__) + +# Configuration constants defined by bazel +NEEDS_EXTERNAL_FILE = "needs_external_needs.json" +BAZEL_OUT_DIR = "bazel-out" + + +def find_workspace_root() -> Path: + """ + Find the Bazel workspace root by looking for the bazel-out directory. + + Returns: + Path to the workspace root directory + """ + current = Path.cwd() + + # Traverse up the directory tree looking for bazel-out + while current != current.parent: + if (current / BAZEL_OUT_DIR).exists(): + return current + current = current.parent + + # If we reach the root without finding it, return current directory + return Path.cwd() + + +def load_external_needs() -> List[Dict[str, Any]]: + """ + Load external needs configuration from JSON file. + + This function reads the needs_external_needs.json file if it exists and + resolves relative paths to absolute paths based on the workspace root. + + Returns: + List of external needs configurations with resolved paths + """ + needs_file = Path(NEEDS_EXTERNAL_FILE) + + if not needs_file.exists(): + logger.debug(f"{NEEDS_EXTERNAL_FILE} not found - no external dependencies") + return [] + + logger.debug(f"Loading external needs from {NEEDS_EXTERNAL_FILE}") + + try: + with needs_file.open("r", encoding="utf-8") as file: + needs_dict = json.load(file) + except json.JSONDecodeError as e: + logger.error(f"Failed to parse {NEEDS_EXTERNAL_FILE}: {e}") + return [] + except Exception as e: + logger.error(f"Failed to read {NEEDS_EXTERNAL_FILE}: {e}") + return [] + + workspace_root = find_workspace_root() + logger.debug(f"Workspace root: {workspace_root}") + + external_needs = [] + for key, config in needs_dict.items(): + if "json_path" not in config: + logger.warning( + f"External needs config for '{key}' missing 'json_path', skipping" + ) + continue + + if "version" not in config: + logger.warning( + f"External needs config for '{key}' missing 'version', skipping" + ) + continue + # Resolve relative path to absolute path + # Bazel provides relative paths like: bazel-out/k8-fastbuild/bin/.../needs.json + # We need absolute paths: .../execroot/_main/bazel-out/... + json_path = workspace_root / config["json_path"] + config["json_path"] = str(json_path) + + logger.debug(f"Added external needs config for '{key}':") + logger.debug(f" json_path: {config['json_path']}") + logger.debug(f" id_prefix: {config.get('id_prefix', 'none')}") + logger.debug(f" version: {config['version']}") + + external_needs.append(config) + + return external_needs + + +def init_external_needs(app: Any, config: Any) -> None: + """ + Initialize external needs configuration. + + Args: + app: Sphinx application object + config: Sphinx configuration object + """ + + config.needs_external_needs = load_external_needs() + + +def setup(app: Any) -> Dict[str, Any]: + """ + Sphinx setup hook to register event listeners. + + Args: + app: Sphinx application object + + Returns: + Extension metadata dictionary + """ + app.connect("config-inited", init_external_needs) + + return { + "version": "1.0", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/bazel/rules/rules_score/templates/conf.template.py b/bazel/rules/rules_score/templates/conf.template.py index c631a5c3..454254ed 100644 --- a/bazel/rules/rules_score/templates/conf.template.py +++ b/bazel/rules/rules_score/templates/conf.template.py @@ -18,8 +18,25 @@ Template variables like {PROJECT_NAME} are replaced during Bazel build. """ -import bazel_sphinx_needs +import json +import os +import sys +from pathlib import Path +from typing import Any, Dict, List +from python.runfiles import Runfiles +from sphinx.util import logging + +# Create a logger with the Sphinx namespace +logger = logging.getLogger(__name__) + +logger.debug("#" * 80) +logger.debug("# READING CONF.PY") +logger.debug("SYSPATH:" + str(sys.path)) +logger.debug("EMV:" + str(os.environ)) + +for k, v in os.environ.items(): + logger.debug(str(k) + ": " + v) # Project configuration - {PROJECT_NAME} will be replaced by the module name during build project = "{PROJECT_NAME}" author = "S-CORE" @@ -31,11 +48,13 @@ # Sphinx extensions - comprehensive list for SCORE modules extensions = [ - "bazel_sphinx_needs", + "sphinx_module_ext", "sphinx_needs", "sphinx_design", "myst_parser", "sphinxcontrib.plantuml", + "trlc", + "clickable_plantuml", ] # MyST parser extensions @@ -45,6 +64,9 @@ exclude_patterns = [ "bazel-*", ".venv*", + # Design-fragment subdirectories (e.g. units/unit_1_design/) are included + # via '.. include::' directives and must not be treated as standalone pages. + "**/*_design", ] # Enable markdown rendering @@ -56,13 +78,79 @@ # Enable numref for cross-references numfig = True -# HTML theme -html_theme = "sphinx_rtd_theme" +# sphinx-needs configuration loaded from the upstream S-CORE metamodel. +# The needs types, extra options, extra links and ID regex are derived +# from score_docs_as_code//src/extensions/score_metamodel:metamodel.yaml +# so they stay in sync with the upstream process description. +# +# Note: score_metamodel is NOT loaded as a Sphinx extension +# (i.e. extensions = [..., "score_metamodel"]) for the following reason: +# When loaded as an extension, score_metamodel registers a build-finished hook +# that runs needs validation via its checks/ modules (mandatory options, +# prohibited words, link pattern checks, etc.). Those check modules do +# bare "from score_metamodel import ..." imports, which require src/extensions/ +# to be on sys.path. That path is only set up by aspect_rules_py's venv +# mechanism, not by the rules_python setup used here. +# Instead, we call load_metamodel_data() directly from yaml_parser — the +# score_docs_as_code+ repo root IS on sys.path, so the import resolves — and +# we get only the type/option/regex data without activating the validation hooks. +try: + from src.extensions.score_metamodel.yaml_parser import ( + load_metamodel_data as _load_metamodel_data, + ) + + _metamodel = _load_metamodel_data() + needs_types = _metamodel.needs_types + needs_extra_options = _metamodel.needs_extra_options + needs_extra_links = _metamodel.needs_extra_links + needs_id_regex = "^[A-Za-z0-9_-]{6,}" +except ImportError: + logger.warning("score_metamodel not available; using minimal needs_types fallback") + needs_types = [] + needs_extra_options = [] + needs_extra_links = [] + needs_id_regex = "^[A-Za-z0-9_-]{6,}" + -# Load external needs and log configuration -needs_external_needs = bazel_sphinx_needs.load_external_needs() -bazel_sphinx_needs.log_config_info(project) +# Use the runfiles to find the plantuml binary. +# Runfiles are only available when running in Bazel. +r = Runfiles.Create() +if r is None: + raise ValueError("Could not initialize Bazel runfiles.") +plantuml_repo_candidates: List[str] = [] -def setup(app): - return bazel_sphinx_needs.setup_sphinx_extension(app, needs_external_needs) +for repo_name in [ + os.environ.get("TEST_WORKSPACE"), + "_main", + "score_tooling", + "score_tooling~", + "score_tooling+", +]: + if repo_name and repo_name not in plantuml_repo_candidates: + plantuml_repo_candidates.append(repo_name) + +plantuml_runfiles_candidates = [ + f"{repo_name}/tools/sphinx/plantuml" for repo_name in plantuml_repo_candidates +] + +plantuml_path = None +for runfile_path in plantuml_runfiles_candidates: + candidate = r.Rlocation(runfile_path, source_repo="") + if candidate and Path(candidate).exists(): + plantuml_path = Path(candidate) + logger.info(f"Selected PlantUML from runfiles path {runfile_path}: {candidate}") + break + +if plantuml_path is None: + searched = ", ".join(plantuml_runfiles_candidates) + raise ValueError( + f"Could not find plantuml binary via runfiles lookup. Searched: {searched}." + ) + +plantuml = str(plantuml_path) +plantuml_output_format = "svg_obj" + +# HTML theme +html_theme = "sphinx_rtd_theme" +logger.debug("#" * 80) diff --git a/bazel/rules/rules_score/templates/seooc_index.template.rst b/bazel/rules/rules_score/templates/dependable_element_index.template.rst similarity index 63% rename from bazel/rules/rules_score/templates/seooc_index.template.rst rename to bazel/rules/rules_score/templates/dependable_element_index.template.rst index b6b43705..70d86b43 100644 --- a/bazel/rules/rules_score/templates/seooc_index.template.rst +++ b/bazel/rules/rules_score/templates/dependable_element_index.template.rst @@ -1,16 +1,3 @@ -.. - # ******************************************************************************* - # 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 - # ******************************************************************************* .. ******************************************************************************* .. Copyright (c) 2025 Contributors to the Eclipse Foundation .. @@ -46,22 +33,21 @@ Assumptions of Use {assumptions_of_use} -Components ----------- +Requirements +------------ .. toctree:: - :maxdepth: 1 - -{components} + :maxdepth: 2 + {requirements} -Units ------ +Components +---------- .. toctree:: - :maxdepth: 1 + :maxdepth: 2 -{units} + {components} diff --git a/bazel/rules/rules_score/templates/fmea.template.rst b/bazel/rules/rules_score/templates/fmea.template.rst new file mode 100644 index 00000000..3dfb889c --- /dev/null +++ b/bazel/rules/rules_score/templates/fmea.template.rst @@ -0,0 +1,8 @@ +{title} +{underline} + +{failure_modes_section} + +{control_measures_section} + +{root_causes_section} diff --git a/bazel/rules/rules_score/test/BUILD b/bazel/rules/rules_score/test/BUILD index de96d34c..f1c26eda 100644 --- a/bazel/rules/rules_score/test/BUILD +++ b/bazel/rules/rules_score/test/BUILD @@ -10,11 +10,11 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("@aspect_rules_py//py:defs.bzl", "py_binary") -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") -load("@rules_python//python:pip.bzl", "compile_pip_requirements") + +load("@rules_python//python:defs.bzl", "py_test") +load("@trlc//:trlc.bzl", "trlc_requirements") load( - "@score_tooling//bazel/rules/rules_score:rules_score.bzl", + "//bazel/rules/rules_score:rules_score.bzl", "architectural_design", "assumptions_of_use", "component", @@ -25,10 +25,7 @@ load( "fmea", "sphinx_module", "unit", -) -load( - "@score_tooling//bazel/rules/rules_score:sphinx_toolchain.bzl", - "sphinx_toolchain", + "unit_design", ) load( ":html_generation_test.bzl", @@ -54,6 +51,12 @@ load( package(default_visibility = ["//visibility:public"]) +filegroup( + name = "bzl_srcs", + srcs = glob(["*.bzl"]), + visibility = ["//bazel/rules/rules_score:__pkg__"], +) + # ============================================================================ # Test Fixtures - Module Definitions # ============================================================================ @@ -65,14 +68,14 @@ sphinx_module( name = "module_c_lib", srcs = glob(["fixtures/module_c/*.rst"]), index = "fixtures/module_c/index.rst", - sphinx = "@score_tooling//bazel/rules/rules_score:score_build", + sphinx = "//bazel/rules/rules_score:score_build", ) sphinx_module( name = "module_b_lib", srcs = glob(["fixtures/module_b/*.rst"]), index = "fixtures/module_b/index.rst", - sphinx = "@score_tooling//bazel/rules/rules_score:score_build", + sphinx = "//bazel/rules/rules_score:score_build", deps = [":module_c_lib"], ) @@ -80,7 +83,7 @@ sphinx_module( name = "module_a_lib", srcs = glob(["fixtures/module_a/*.rst"]), index = "fixtures/module_a/index.rst", - sphinx = "@score_tooling//bazel/rules/rules_score:score_build", + sphinx = "//bazel/rules/rules_score:score_build", deps = [ ":module_b_lib", ":module_c_lib", @@ -93,22 +96,42 @@ sphinx_module( # - Feature Requirements: wp__requirements_feat # TODO: Feature requirements are a stand-alone artifact for now # We have to link them manually to component requirements +trlc_requirements( + name = "asr_fixtures", + srcs = ["fixtures/seooc_test/asr_fixtures.trlc"], + spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], +) + +trlc_requirements( + name = "feat_req_trlc", + srcs = ["fixtures/seooc_test/feat_req_fixtures.trlc"], + spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], + deps = [":asr_fixtures"], +) + feature_requirements( name = "feat_req", - srcs = ["fixtures/seooc_test/feature_requirements.rst"], + srcs = [":feat_req_trlc"], ) # - Component Requirements: wp__requirements_comp +trlc_requirements( + name = "comp_req_trlc", + srcs = ["fixtures/seooc_test/comp_req_fixtures.trlc"], + spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], + deps = [":feat_req_trlc"], +) + component_requirements( name = "comp_req", - srcs = ["fixtures/seooc_test/component_requirements.rst"], + srcs = [":comp_req_trlc"], ) # - Assumptions of Use: wp__requirements_comp_aou assumptions_of_use( name = "aous", srcs = ["fixtures/seooc_test/assumptions_of_use.rst"], - feature_requirement = [":feat_req"], + requirements = [":feat_req"], ) # - Architecture Design: wp__component_arch @@ -118,21 +141,26 @@ architectural_design( static = ["fixtures/seooc_test/static_architecture.rst"], ) +unit_design( + name = "unit_arch_design", + static = ["fixtures/seooc_test/static_architecture.rst"], +) + # - Safety Analysis (DFA): wp__sw_component_dfa # - Safety Analysis (FMEA): wp__sw_component_fmea dependability_analysis( name = "dependability_analysis_target", arch_design = ":arch_design", dfa = ["fixtures/seooc_test/dfa.rst"], - safety_analysis = [":samplelibrary_safety_analysis"], + fmea = [":samplelibrary_fmea"], ) fmea( - name = "samplelibrary_safety_analysis", - # TODO: add failure modes, control measures, and root causes here - # failuremodes = [], + name = "samplelibrary_fmea", + # TODO # controlmeasures = [], # can be AoUs or requirements - # root_causes = [], + # failuremodes = [], + # fta = [], arch_design = ":arch_design", ) @@ -143,11 +171,9 @@ dependable_element( components = [], dependability_analysis = [":dependability_analysis_target"], description = "Test SEooC module demonstrating S-CORE process compliance structure.", - requirements = [":comp_req"], + integrity_level = "B", + requirements = [":feat_req"], tests = [], - deps = [ - ":module_c_lib", # dependency to other seoocs/score_components - ], ) # ============================================================================ @@ -182,6 +208,7 @@ cc_test( deps = [ ":mock_lib1", ":mock_lib2", + "@googletest//:gtest_main", ], ) @@ -189,19 +216,33 @@ cc_test( unit( name = "test_unit", testonly = True, + scope = ["//bazel/rules/rules_score/test:__pkg__"], tests = [":test_unit_tests"], - unit_design = [":arch_design"], + unit_design = [":unit_arch_design"], implementation = [ ":mock_lib1", ":mock_lib2", ], ) +cc_test( + name = "test_binary_unit_tests", + testonly = True, + srcs = ["fixtures/test_binary_unit_test.cc"], + tags = ["manual"], + deps = [ + ":mock_lib1", + ":mock_lib2", + "@googletest//:gtest_main", + ], +) + unit( name = "test_binary_unit", testonly = True, - tests = [":test_unit_tests"], - unit_design = [":arch_design"], + scope = ["//bazel/rules/rules_score/test:__pkg__"], + tests = [":test_binary_unit_tests"], + unit_design = [":unit_arch_design"], implementation = [":test_component_binary"], ) @@ -226,7 +267,8 @@ dependable_element( components = [":test_component"], dependability_analysis = [":dependability_analysis_target"], description = "Test dependable element for unit testing", - requirements = [":comp_req"], + integrity_level = "B", + requirements = [":feat_req"], tests = [], # Empty for testing ) @@ -243,17 +285,22 @@ cc_library( cc_test( name = "test_unit2_tests", testonly = True, - srcs = ["fixtures/test_unit_test.cc"], + srcs = ["fixtures/test_unit2_test.cc"], tags = ["manual"], - deps = [":mock_lib3"], + deps = [ + ":mock_lib2", + ":mock_lib3", + "@googletest//:gtest_main", + ], ) # Second unit that will be shared between components unit( name = "test_unit2", testonly = True, + scope = ["//bazel/rules/rules_score/test:__pkg__"], tests = [":test_unit2_tests"], - unit_design = [":arch_design"], + unit_design = [":unit_arch_design"], implementation = [":mock_lib3"], ) @@ -272,7 +319,6 @@ component( testonly = True, components = [ ":test_nested_component", - ":test_unit2", # Same unit appears here and in nested component ":test_unit", # Different unit ], requirements = [":comp_req"], @@ -288,7 +334,8 @@ dependable_element( components = [":test_parent_component"], dependability_analysis = [":dependability_analysis_target"], description = "Test dependable element with nested components for testing recursive unit collection and deduplication", - requirements = [":comp_req"], + integrity_level = "B", + requirements = [":feat_req"], tests = [], ) @@ -313,19 +360,6 @@ html_merging_test( target_under_test = ":module_a_lib", ) -# ============================================================================ -# HTML Content Validation Tests -# ============================================================================ - -# Test that generated HTML does not contain "Unknown need" statements -sh_test( - name = "no_unknown_needs_test", - srcs = ["check_unknown_needs.sh"], - # args = ["$(location //:module_a_lib/html/index.html)"], - data = [":module_a_lib"], - tags = ["manual"], -) - # ============================================================================ # SEooC-Specific Tests # ============================================================================ @@ -345,7 +379,7 @@ seooc_sphinx_module_generated_test( # Test that needs provider exists for cross-referencing seooc_needs_provider_test( name = "seooc_tests_needs_provider", - target_under_test = ":seooc_test_lib_needs", + target_under_test = ":seooc_test_lib_doc_needs", ) # ============================================================================ @@ -394,6 +428,17 @@ component_sphinx_sources_test( # Unit, Component, and Dependable Element test suite unit_component_test_suite(name = "unit_component_tests") +# ============================================================================ +# FTA Linker Unit Tests +# ============================================================================ + +py_test( + name = "test_safety_analysis_tools", + size = "small", + srcs = ["test_safety_analysis_tools.py"], + deps = ["//bazel/rules/rules_score:safety_analysis_tools"], +) + # ============================================================================ # Combined Test Suite # ============================================================================ @@ -404,57 +449,7 @@ test_suite( tests = [ ":seooc_tests", ":sphinx_module_tests", + ":test_safety_analysis_tools", ":unit_component_tests", ], ) - -# ============================================================================ -# Toolchain for the score build -# ============================================================================ -py_binary( - name = "score_build", - srcs = ["@score_tooling//bazel/rules/rules_score:src/sphinx_wrapper.py"], - data = [], - env = { - "SOURCE_DIRECTORY": "", - "DATA": "", - "ACTION": "check", - }, - main = "@score_tooling//bazel/rules/rules_score:src/sphinx_wrapper.py", - visibility = ["//visibility:public"], - deps = [ - "@score_docs_as_code//src:plantuml_for_python", - "@score_docs_as_code//src/extensions/score_sphinx_bundle", - "@score_tooling//bazel/rules/rules_score:sphinx_module_ext", - ], -) - -sphinx_toolchain( - name = "score_sphinx_toolchain", - conf_template = "template/conf.template.py", - sphinx = ":score_build", -) - -toolchain( - name = "score_toolchain", - exec_compatible_with = [ - "@platforms//os:linux", - "@platforms//cpu:x86_64", - ], - target_compatible_with = [ - "@platforms//os:linux", - "@platforms//cpu:x86_64", - ], - toolchain = ":score_sphinx_toolchain", - toolchain_type = "@score_tooling//bazel/rules/rules_score:toolchain_type", - visibility = ["//visibility:public"], -) - -compile_pip_requirements( - name = "requirements", - src = "//:requirements.in", - requirements_txt = "//:requirements.txt", - tags = [ - "manual", - ], -) diff --git a/bazel/rules/rules_score/test/check_unknown_needs.sh b/bazel/rules/rules_score/test/check_unknown_needs.sh old mode 100755 new mode 100644 diff --git a/bazel/rules/rules_score/test/fixtures/mock_test.sh b/bazel/rules/rules_score/test/fixtures/mock_test.sh old mode 100755 new mode 100644 diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/asr_fixtures.trlc b/bazel/rules/rules_score/test/fixtures/seooc_test/asr_fixtures.trlc new file mode 100644 index 00000000..2a08c183 --- /dev/null +++ b/bazel/rules/rules_score/test/fixtures/seooc_test/asr_fixtures.trlc @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SeoocTest + +import ScoreReq + +ScoreReq.AssumedSystemReq ASR_001 { + description = "The system shall process data" + safety = ScoreReq.Asil.QM + rationale = "Required for basic functionality" + version = 1 +} diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/comp_req_fixtures.trlc b/bazel/rules/rules_score/test/fixtures/seooc_test/comp_req_fixtures.trlc new file mode 100644 index 00000000..97be39b5 --- /dev/null +++ b/bazel/rules/rules_score/test/fixtures/seooc_test/comp_req_fixtures.trlc @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SeoocTest + +import ScoreReq + +ScoreReq.CompReq COMP_001 { + description = "The component shall process input data within defined latency bounds" + safety = ScoreReq.Asil.QM + derived_from = [SeoocTest.FEAT_001@1] + version = 1 +} diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/feat_req_fixtures.trlc b/bazel/rules/rules_score/test/fixtures/seooc_test/feat_req_fixtures.trlc new file mode 100644 index 00000000..a9b85aef --- /dev/null +++ b/bazel/rules/rules_score/test/fixtures/seooc_test/feat_req_fixtures.trlc @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SeoocTest + +import ScoreReq + +ScoreReq.FeatReq FEAT_001 { + description = "The component shall process input data" + safety = ScoreReq.Asil.QM + derived_from = [SeoocTest.ASR_001@1] + version = 1 +} diff --git a/bazel/rules/rules_score/test/requirements.in b/bazel/rules/rules_score/test/requirements.in deleted file mode 100644 index 05d941ed..00000000 --- a/bazel/rules/rules_score/test/requirements.in +++ /dev/null @@ -1,8 +0,0 @@ -pytest -basedpyright -sphinx -sphinx-needs -sphinx-design -myst-parser -sphinxcontrib-plantuml -sphinx-rtd-theme diff --git a/bazel/rules/rules_score/test/requirements.txt b/bazel/rules/rules_score/test/requirements.txt deleted file mode 100644 index 8666593e..00000000 --- a/bazel/rules/rules_score/test/requirements.txt +++ /dev/null @@ -1,509 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# bazel run //:requirements.update -# -alabaster==1.0.0 \ - --hash=sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e \ - --hash=sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b - # via sphinx -babel==2.18.0 \ - --hash=sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d \ - --hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35 - # via sphinx -basedpyright==1.38.3 \ - --hash=sha256:1f15c2e489c67d6c5e896c24b6a63251195c04223a55e4568b8f8e8ed49ca830 \ - --hash=sha256:9725419786afbfad8a9539527f162da02d462afad440b0412fdb3f3cdf179b90 - # via -r requirements.in -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 - # via requests -charset-normalizer==3.4.6 \ - --hash=sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e \ - --hash=sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c \ - --hash=sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5 \ - --hash=sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815 \ - --hash=sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f \ - --hash=sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0 \ - --hash=sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484 \ - --hash=sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407 \ - --hash=sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6 \ - --hash=sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8 \ - --hash=sha256:1ed80ff870ca6de33f4d953fda4d55654b9a2b340ff39ab32fa3adbcd718f264 \ - --hash=sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815 \ - --hash=sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2 \ - --hash=sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4 \ - --hash=sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579 \ - --hash=sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f \ - --hash=sha256:2bd9d128ef93637a5d7a6af25363cf5dec3fa21cf80e68055aad627f280e8afa \ - --hash=sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95 \ - --hash=sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab \ - --hash=sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297 \ - --hash=sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a \ - --hash=sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e \ - --hash=sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84 \ - --hash=sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8 \ - --hash=sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0 \ - --hash=sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9 \ - --hash=sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f \ - --hash=sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1 \ - --hash=sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843 \ - --hash=sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565 \ - --hash=sha256:461598cd852bfa5a61b09cae2b1c02e2efcd166ee5516e243d540ac24bfa68a7 \ - --hash=sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c \ - --hash=sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b \ - --hash=sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7 \ - --hash=sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687 \ - --hash=sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9 \ - --hash=sha256:517ad0e93394ac532745129ceabdf2696b609ec9f87863d337140317ebce1c14 \ - --hash=sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89 \ - --hash=sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f \ - --hash=sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0 \ - --hash=sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9 \ - --hash=sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a \ - --hash=sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389 \ - --hash=sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0 \ - --hash=sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30 \ - --hash=sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd \ - --hash=sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e \ - --hash=sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9 \ - --hash=sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc \ - --hash=sha256:659a1e1b500fac8f2779dd9e1570464e012f43e580371470b45277a27baa7532 \ - --hash=sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d \ - --hash=sha256:69dd852c2f0ad631b8b60cfbe25a28c0058a894de5abb566619c205ce0550eae \ - --hash=sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2 \ - --hash=sha256:71be7e0e01753a89cf024abf7ecb6bca2c81738ead80d43004d9b5e3f1244e64 \ - --hash=sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f \ - --hash=sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557 \ - --hash=sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e \ - --hash=sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff \ - --hash=sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398 \ - --hash=sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db \ - --hash=sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a \ - --hash=sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43 \ - --hash=sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597 \ - --hash=sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c \ - --hash=sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e \ - --hash=sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2 \ - --hash=sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54 \ - --hash=sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e \ - --hash=sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4 \ - --hash=sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4 \ - --hash=sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7 \ - --hash=sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6 \ - --hash=sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5 \ - --hash=sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194 \ - --hash=sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69 \ - --hash=sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f \ - --hash=sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316 \ - --hash=sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e \ - --hash=sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73 \ - --hash=sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8 \ - --hash=sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923 \ - --hash=sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88 \ - --hash=sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f \ - --hash=sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21 \ - --hash=sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4 \ - --hash=sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6 \ - --hash=sha256:ab30e5e3e706e3063bc6de96b118688cb10396b70bb9864a430f67df98c61ecc \ - --hash=sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2 \ - --hash=sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866 \ - --hash=sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021 \ - --hash=sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2 \ - --hash=sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d \ - --hash=sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8 \ - --hash=sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de \ - --hash=sha256:bf625105bb9eef28a56a943fec8c8a98aeb80e7d7db99bd3c388137e6eb2d237 \ - --hash=sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4 \ - --hash=sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778 \ - --hash=sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb \ - --hash=sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc \ - --hash=sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602 \ - --hash=sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4 \ - --hash=sha256:d08ec48f0a1c48d75d0356cea971921848fb620fdeba805b28f937e90691209f \ - --hash=sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5 \ - --hash=sha256:d5f5d1e9def3405f60e3ca8232d56f35c98fb7bf581efcc60051ebf53cb8b611 \ - --hash=sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8 \ - --hash=sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf \ - --hash=sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d \ - --hash=sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b \ - --hash=sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db \ - --hash=sha256:df01808ee470038c3f8dc4f48620df7225c49c2d6639e38f96e6d6ac6e6f7b0e \ - --hash=sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077 \ - --hash=sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd \ - --hash=sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef \ - --hash=sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e \ - --hash=sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8 \ - --hash=sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe \ - --hash=sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058 \ - --hash=sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17 \ - --hash=sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833 \ - --hash=sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421 \ - --hash=sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550 \ - --hash=sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff \ - --hash=sha256:f50498891691e0864dc3da965f340fada0771f6142a378083dc4608f4ea513e2 \ - --hash=sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc \ - --hash=sha256:f61aa92e4aad0be58eb6eb4e0c21acf32cf8065f4b2cae5665da756c4ceef982 \ - --hash=sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d \ - --hash=sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed \ - --hash=sha256:f98059e4fcd3e3e4e2d632b7cf81c2faae96c43c60b569e9c621468082f1d104 \ - --hash=sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659 - # via requests -docutils==0.22.4 \ - --hash=sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968 \ - --hash=sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de - # via - # myst-parser - # sphinx - # sphinx-rtd-theme -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 - # via requests -imagesize==2.0.0 \ - --hash=sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96 \ - --hash=sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3 - # via sphinx -iniconfig==2.3.0 \ - --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ - --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 - # via pytest -jinja2==3.1.6 \ - --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ - --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 - # via - # myst-parser - # sphinx -jsonschema-rs==0.37.4 \ - --hash=sha256:03b34f911e99343fc388651688683010daee538a3cf8cf86a7997bca28fdf16b \ - --hash=sha256:0f17a61deb557faa57dffb9596e4f022873404f935114367788b1eebdec2bb00 \ - --hash=sha256:10fd978a145a6f8d11373879e7d0ff232b409f77c7faf608e6b4549a7f90aaed \ - --hash=sha256:1d3f8c8b376966c19fd4183fa979dbadc9fdd6070f2bfa4d127bdf70946963cc \ - --hash=sha256:393ece7037a0d19fd528f7a67a32749453876468871a0bd2267909a57d8d4e32 \ - --hash=sha256:5975e448092e99d6cc60793a71f0fee516dbf0fd1e6d2f6f1e4689627268f344 \ - --hash=sha256:67f36f1c445c70f9975d17a84ce37f79593f6234d7eb292830d7749e5fa58ff4 \ - --hash=sha256:75f3b4e0707dcb3dccf911ff49e387b4db54957fe1a19d3423015a65e3762057 \ - --hash=sha256:a56d154b638deb947dbd0dfc285c349eb23a877221f2b0496a2dfa25948cc239 \ - --hash=sha256:dedf72e5e673e3af5b9925979fd71484debada61fb7a3dfabf9bbc74b8012664 \ - --hash=sha256:e159075b1846718466998d5a9294c661113b347b8b4749767680a97c8ed2bf4d \ - --hash=sha256:e93a8720f6e858d872dc2882e7d3b3243ee76f7aa4d60048272773d44df466e7 - # via sphinx-needs -markdown-it-py==4.0.0 \ - --hash=sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147 \ - --hash=sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3 - # via - # mdit-py-plugins - # myst-parser -markupsafe==3.0.3 \ - --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ - --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ - --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ - --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ - --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ - --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ - --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ - --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ - --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ - --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ - --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ - --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ - --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ - --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ - --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ - --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ - --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ - --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ - --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ - --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ - --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ - --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ - --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ - --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ - --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ - --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ - --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ - --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ - --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ - --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ - --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ - --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ - --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ - --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ - --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ - --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ - --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ - --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ - --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ - --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ - --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ - --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ - --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ - --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ - --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ - --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ - --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ - --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ - --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ - --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ - --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ - --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ - --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ - --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ - --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ - --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ - --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ - --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ - --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ - --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ - --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ - --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ - --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ - --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ - --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ - --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ - --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ - --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ - --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ - --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ - --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ - --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ - --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ - --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ - --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ - --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ - --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ - --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ - --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ - --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ - --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ - --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ - --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ - --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ - --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ - --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ - --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ - --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ - --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 - # via jinja2 -mdit-py-plugins==0.5.0 \ - --hash=sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f \ - --hash=sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6 - # via myst-parser -mdurl==0.1.2 \ - --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ - --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba - # via markdown-it-py -minijinja==2.18.0 \ - --hash=sha256:01cd85bdae15c8ce44afd0cadb07808a0b0f55363f3ee3f9459c4e1663707095 \ - --hash=sha256:0b32f66d7138e6eefeabca39583a9d98e3bf8c8c7ebe0e287250dff436f4234f \ - --hash=sha256:168de945c4fe2a12f17e9190b80780646b68e487c341fcde8cba32680f7869a8 \ - --hash=sha256:1928d1522fb53adf50bf5a781068e8ad1b925f0f03579b48b1e98e7ae7526feb \ - --hash=sha256:1bb6a6be7851961a9192b158d436ad6c7b8b6d58e620c5da9d638a7e2f4a6b6d \ - --hash=sha256:2fb4e8a94e9f8337f894842084570e290a1cc39fb306cab457d405a129cf1fc7 \ - --hash=sha256:42daaf7310956b491f50ea356b751ed12d9ebb23e82e7136871afd19f87aab80 \ - --hash=sha256:8b252458a3a51dc09bef48422f68595684984785e40300b426e318c5916c2704 \ - --hash=sha256:c3ccdc1d48939068c08962e6051541fd9a3af67d20ceb08262a540ea9364dcf7 \ - --hash=sha256:d77e7bce8fe2ddd3dfa13f142b2e8d858c3a1ea77c927337a2c9eade0da227bc \ - --hash=sha256:f2116a9b0f1211d42d6b148bb6abd34695b0351f5c8cfa8533a21e7cf109d090 \ - --hash=sha256:f6e9ac8256fc5453e2c5ab91a44201447ea4975c341535a7aa623447b88b7c4e - # via sphinx-needs -myst-parser==5.0.0 \ - --hash=sha256:ab31e516024918296e169139072b81592336f2fef55b8986aa31c9f04b5f7211 \ - --hash=sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a - # via -r requirements.in -nodejs-wheel-binaries==24.14.0 \ - --hash=sha256:222dbf516ccc877afcad4e4789a81b4ee93daaa9f0ad97c464417d9597f49449 \ - --hash=sha256:348fa061b57625de7250d608e2d9b7c4bc170544da7e328325343860eadd59e5 \ - --hash=sha256:519507fb74f3f2b296ab1e9f00dcc211f36bbfb93c60229e72dcdee9dafd301a \ - --hash=sha256:59bb78b8eb08c3e32186da1ef913f1c806b5473d8bd0bb4492702092747b674a \ - --hash=sha256:60b83c4e98b0c7d836ac9ccb67dcb36e343691cbe62cd325799ff9ed936286f3 \ - --hash=sha256:68c93c52ff06d704bcb5ed160b4ba04ab1b291d238aaf996b03a5396e0e9a7ed \ - --hash=sha256:78a9bd1d6b11baf1433f9fb84962ff8aa71c87d48b6434f98224bc49a2253a6e \ - --hash=sha256:b35d6fcccfe4fb0a409392d237fbc67796bac0d357b996bc12d057a1531a238b \ - --hash=sha256:c87b515e44b0e4a523017d8c59f26ccbd05b54fe593338582825d4b51fc91e1c - # via basedpyright -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 - # via - # pytest - # sphinx -pluggy==1.6.0 \ - --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ - --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 - # via pytest -pygments==2.19.2 \ - --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \ - --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b - # via - # pytest - # sphinx -pytest==9.0.2 \ - --hash=sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b \ - --hash=sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11 - # via -r requirements.in -pyyaml==6.0.3 \ - --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ - --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ - --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ - --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ - --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ - --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ - --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ - --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ - --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ - --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ - --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ - --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ - --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ - --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ - --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ - --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ - --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ - --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ - --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ - --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ - --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ - --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ - --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ - --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ - --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ - --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ - --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ - --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ - --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ - --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ - --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ - --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ - --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ - --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ - --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ - --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ - --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ - --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ - --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ - --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ - --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ - --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ - --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ - --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ - --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ - --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ - --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ - --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ - --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ - --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ - --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ - --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ - --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ - --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ - --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ - --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ - --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ - --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ - --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ - --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ - --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ - --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ - --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ - --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ - --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ - --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ - --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ - --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ - --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ - --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ - --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ - --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ - --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 - # via myst-parser -requests==2.32.5 \ - --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ - --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf - # via - # requests-file - # sphinx - # sphinx-needs -requests-file==2.1.0 \ - --hash=sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658 \ - --hash=sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c - # via sphinx-needs -roman-numerals==4.1.0 \ - --hash=sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2 \ - --hash=sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7 - # via sphinx -snowballstemmer==3.0.1 \ - --hash=sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064 \ - --hash=sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895 - # via sphinx -sphinx==9.1.0 \ - --hash=sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb \ - --hash=sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978 - # via - # -r requirements.in - # myst-parser - # sphinx-data-viewer - # sphinx-design - # sphinx-needs - # sphinx-rtd-theme - # sphinxcontrib-jquery - # sphinxcontrib-plantuml -sphinx-data-viewer==0.1.5 \ - --hash=sha256:a7d5e58613562bb745380bfe61ca8b69997998167fd6fa9aea55606c9a4b17e4 \ - --hash=sha256:b74b1d304c505c464d07c7b225ed0d84ea02dcc88bc1c49cdad7c2275fbbdad4 - # via sphinx-needs -sphinx-design==0.7.0 \ - --hash=sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a \ - --hash=sha256:f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282 - # via -r requirements.in -sphinx-needs==8.0.0 \ - --hash=sha256:540c380c074d4088a557ea353e91513bfc1cb7712b10925c13ac9e5ebb7be091 \ - --hash=sha256:c4336ee0e3c949eff9eb11a14910f7b6b68cb8284d731cfddf97694037337674 - # via -r requirements.in -sphinx-rtd-theme==3.1.0 \ - --hash=sha256:1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89 \ - --hash=sha256:b44276f2c276e909239a4f6c955aa667aaafeb78597923b1c60babc76db78e4c - # via -r requirements.in -sphinxcontrib-applehelp==2.0.0 \ - --hash=sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1 \ - --hash=sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5 - # via sphinx -sphinxcontrib-devhelp==2.0.0 \ - --hash=sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad \ - --hash=sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2 - # via sphinx -sphinxcontrib-htmlhelp==2.1.0 \ - --hash=sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8 \ - --hash=sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9 - # via sphinx -sphinxcontrib-jquery==4.1 \ - --hash=sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a \ - --hash=sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae - # via - # sphinx-needs - # sphinx-rtd-theme -sphinxcontrib-jsmath==1.0.1 \ - --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \ - --hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8 - # via sphinx -sphinxcontrib-plantuml==0.31 \ - --hash=sha256:fd74752f8ea070e641c3f8a402fccfa1d4a4056e0967b56033d2a76282d9f956 - # via -r requirements.in -sphinxcontrib-qthelp==2.0.0 \ - --hash=sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab \ - --hash=sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb - # via sphinx -sphinxcontrib-serializinghtml==2.0.0 \ - --hash=sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 \ - --hash=sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d - # via sphinx -typing-extensions==4.15.0 \ - --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ - --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 - # via sphinx-needs -urllib3==2.6.3 \ - --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ - --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 - # via requests diff --git a/bazel/rules/rules_score/test/test/BUILD b/bazel/rules/rules_score/test/test/BUILD new file mode 100644 index 00000000..f1c26eda --- /dev/null +++ b/bazel/rules/rules_score/test/test/BUILD @@ -0,0 +1,455 @@ +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* + +load("@rules_python//python:defs.bzl", "py_test") +load("@trlc//:trlc.bzl", "trlc_requirements") +load( + "//bazel/rules/rules_score:rules_score.bzl", + "architectural_design", + "assumptions_of_use", + "component", + "component_requirements", + "dependability_analysis", + "dependable_element", + "feature_requirements", + "fmea", + "sphinx_module", + "unit", + "unit_design", +) +load( + ":html_generation_test.bzl", + "html_merging_test", + "module_dependencies_test", + "needs_transitive_test", + "sphinx_module_test_suite", +) +load( + ":seooc_test.bzl", + "seooc_artifacts_copied_test", + "seooc_needs_provider_test", + "seooc_sphinx_module_generated_test", +) +load( + ":unit_component_test.bzl", + "component_provider_test", + "component_sphinx_sources_test", + "unit_component_test_suite", + "unit_provider_test", + "unit_sphinx_sources_test", +) + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "bzl_srcs", + srcs = glob(["*.bzl"]), + visibility = ["//bazel/rules/rules_score:__pkg__"], +) + +# ============================================================================ +# Test Fixtures - Module Definitions +# ============================================================================ + +# Test 1: Multi-Module Aggregation +# Dependency graph: module_a_lib -> module_b_lib -> module_c_lib +# module_a_lib -> module_c_lib (also direct) +sphinx_module( + name = "module_c_lib", + srcs = glob(["fixtures/module_c/*.rst"]), + index = "fixtures/module_c/index.rst", + sphinx = "//bazel/rules/rules_score:score_build", +) + +sphinx_module( + name = "module_b_lib", + srcs = glob(["fixtures/module_b/*.rst"]), + index = "fixtures/module_b/index.rst", + sphinx = "//bazel/rules/rules_score:score_build", + deps = [":module_c_lib"], +) + +sphinx_module( + name = "module_a_lib", + srcs = glob(["fixtures/module_a/*.rst"]), + index = "fixtures/module_a/index.rst", + sphinx = "//bazel/rules/rules_score:score_build", + deps = [ + ":module_b_lib", + ":module_c_lib", + ], +) + +# Test 2: SEooC (Safety Element out of Context) Module +# Tests the score_component macro with S-CORE process artifacts + +# - Feature Requirements: wp__requirements_feat +# TODO: Feature requirements are a stand-alone artifact for now +# We have to link them manually to component requirements +trlc_requirements( + name = "asr_fixtures", + srcs = ["fixtures/seooc_test/asr_fixtures.trlc"], + spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], +) + +trlc_requirements( + name = "feat_req_trlc", + srcs = ["fixtures/seooc_test/feat_req_fixtures.trlc"], + spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], + deps = [":asr_fixtures"], +) + +feature_requirements( + name = "feat_req", + srcs = [":feat_req_trlc"], +) + +# - Component Requirements: wp__requirements_comp +trlc_requirements( + name = "comp_req_trlc", + srcs = ["fixtures/seooc_test/comp_req_fixtures.trlc"], + spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], + deps = [":feat_req_trlc"], +) + +component_requirements( + name = "comp_req", + srcs = [":comp_req_trlc"], +) + +# - Assumptions of Use: wp__requirements_comp_aou +assumptions_of_use( + name = "aous", + srcs = ["fixtures/seooc_test/assumptions_of_use.rst"], + requirements = [":feat_req"], +) + +# - Architecture Design: wp__component_arch +architectural_design( + name = "arch_design", + dynamic = ["fixtures/seooc_test/dynamic_architecture.rst"], + static = ["fixtures/seooc_test/static_architecture.rst"], +) + +unit_design( + name = "unit_arch_design", + static = ["fixtures/seooc_test/static_architecture.rst"], +) + +# - Safety Analysis (DFA): wp__sw_component_dfa +# - Safety Analysis (FMEA): wp__sw_component_fmea +dependability_analysis( + name = "dependability_analysis_target", + arch_design = ":arch_design", + dfa = ["fixtures/seooc_test/dfa.rst"], + fmea = [":samplelibrary_fmea"], +) + +fmea( + name = "samplelibrary_fmea", + # TODO + # controlmeasures = [], # can be AoUs or requirements + # failuremodes = [], + # fta = [], + arch_design = ":arch_design", +) + +dependable_element( + name = "seooc_test_lib", + architectural_design = [":arch_design"], + assumptions_of_use = [":aous"], + components = [], + dependability_analysis = [":dependability_analysis_target"], + description = "Test SEooC module demonstrating S-CORE process compliance structure.", + integrity_level = "B", + requirements = [":feat_req"], + tests = [], +) + +# ============================================================================ +# Test Fixtures - Unit, Component, and Dependable Element +# ============================================================================ + +# Mock implementation targets with dummy functions +cc_library( + name = "mock_lib1", + srcs = ["fixtures/mock_lib1.cc"], +) + +cc_library( + name = "mock_lib2", + srcs = ["fixtures/mock_lib2.cc"], +) + +cc_binary( + name = "test_component_binary", + srcs = ["fixtures/test_component_main.cc"], + deps = [ + ":mock_lib1", + ":mock_lib2", + ], +) + +cc_test( + name = "test_unit_tests", + testonly = True, + srcs = ["fixtures/test_unit_test.cc"], + tags = ["manual"], + deps = [ + ":mock_lib1", + ":mock_lib2", + "@googletest//:gtest_main", + ], +) + +# Test Unit +unit( + name = "test_unit", + testonly = True, + scope = ["//bazel/rules/rules_score/test:__pkg__"], + tests = [":test_unit_tests"], + unit_design = [":unit_arch_design"], + implementation = [ + ":mock_lib1", + ":mock_lib2", + ], +) + +cc_test( + name = "test_binary_unit_tests", + testonly = True, + srcs = ["fixtures/test_binary_unit_test.cc"], + tags = ["manual"], + deps = [ + ":mock_lib1", + ":mock_lib2", + "@googletest//:gtest_main", + ], +) + +unit( + name = "test_binary_unit", + testonly = True, + scope = ["//bazel/rules/rules_score/test:__pkg__"], + tests = [":test_binary_unit_tests"], + unit_design = [":unit_arch_design"], + implementation = [":test_component_binary"], +) + +# Test Component +component( + name = "test_component", + testonly = True, + components = [ + ":test_unit", + "test_binary_unit", + ], + requirements = [":comp_req"], + tests = [], # Empty for testing +) + +# Test Dependable Element +dependable_element( + name = "test_dependable_element", + testonly = True, + architectural_design = [":arch_design"], + assumptions_of_use = [":aous"], + components = [":test_component"], + dependability_analysis = [":dependability_analysis_target"], + description = "Test dependable element for unit testing", + integrity_level = "B", + requirements = [":feat_req"], + tests = [], # Empty for testing +) + +# ============================================================================ +# Test Fixtures - Nested Components for Recursive Testing +# ============================================================================ + +# Additional mock implementations +cc_library( + name = "mock_lib3", + srcs = ["fixtures/mock_lib1.cc"], # Reuse same source for testing +) + +cc_test( + name = "test_unit2_tests", + testonly = True, + srcs = ["fixtures/test_unit2_test.cc"], + tags = ["manual"], + deps = [ + ":mock_lib2", + ":mock_lib3", + "@googletest//:gtest_main", + ], +) + +# Second unit that will be shared between components +unit( + name = "test_unit2", + testonly = True, + scope = ["//bazel/rules/rules_score/test:__pkg__"], + tests = [":test_unit2_tests"], + unit_design = [":unit_arch_design"], + implementation = [":mock_lib3"], +) + +# Nested component containing unit2 +component( + name = "test_nested_component", + testonly = True, + components = [":test_unit2"], + requirements = [":comp_req"], + tests = [], +) + +# Parent component containing nested component and shared unit +component( + name = "test_parent_component", + testonly = True, + components = [ + ":test_nested_component", + ":test_unit", # Different unit + ], + requirements = [":comp_req"], + tests = [], +) + +# Dependable element with nested components to test recursive collection +dependable_element( + name = "test_dependable_element_nested", + testonly = True, + architectural_design = [":arch_design"], + assumptions_of_use = [":aous"], + components = [":test_parent_component"], + dependability_analysis = [":dependability_analysis_target"], + description = "Test dependable element with nested components for testing recursive unit collection and deduplication", + integrity_level = "B", + requirements = [":feat_req"], + tests = [], +) + +# ============================================================================ +# Test Instantiations - HTML Generation Tests +# ============================================================================ + +# Needs Generation Tests +needs_transitive_test( + name = "needs_transitive_test", + target_under_test = ":module_b_lib_needs", +) + +# Dependency Tests +module_dependencies_test( + name = "module_dependencies_test", + target_under_test = ":module_a_lib", +) + +html_merging_test( + name = "html_merging_test", + target_under_test = ":module_a_lib", +) + +# ============================================================================ +# SEooC-Specific Tests +# ============================================================================ + +# Test that all artifacts are copied +seooc_artifacts_copied_test( + name = "seooc_tests_artifacts_copied", + target_under_test = ":seooc_test_lib_index", +) + +# Test that sphinx_module is generated with correct providers +seooc_sphinx_module_generated_test( + name = "seooc_tests_sphinx_module_generated", + target_under_test = ":seooc_test_lib", +) + +# Test that needs provider exists for cross-referencing +seooc_needs_provider_test( + name = "seooc_tests_needs_provider", + target_under_test = ":seooc_test_lib_doc_needs", +) + +# ============================================================================ +# Test Suites +# ============================================================================ + +# Main test suite combining all sphinx_module tests +sphinx_module_test_suite(name = "sphinx_module_tests") + +# SEooC-focused test suite +test_suite( + name = "seooc_tests", + tests = [ + ":seooc_tests_artifacts_copied", + ":seooc_tests_needs_provider", + ":seooc_tests_sphinx_module_generated", + ], +) + +# ============================================================================ +# Unit, Component, and Dependable Element Test Instantiations +# ============================================================================ + +# Unit tests +unit_provider_test( + name = "unit_provider_test", + target_under_test = ":test_unit", +) + +unit_sphinx_sources_test( + name = "unit_sphinx_sources_test", + target_under_test = ":test_unit", +) + +# Component tests +component_provider_test( + name = "component_provider_test", + target_under_test = ":test_component", +) + +component_sphinx_sources_test( + name = "component_sphinx_sources_test", + target_under_test = ":test_component", +) + +# Unit, Component, and Dependable Element test suite +unit_component_test_suite(name = "unit_component_tests") + +# ============================================================================ +# FTA Linker Unit Tests +# ============================================================================ + +py_test( + name = "test_safety_analysis_tools", + size = "small", + srcs = ["test_safety_analysis_tools.py"], + deps = ["//bazel/rules/rules_score:safety_analysis_tools"], +) + +# ============================================================================ +# Combined Test Suite +# ============================================================================ + +# Combined test suite for all tests +test_suite( + name = "all_tests", + tests = [ + ":seooc_tests", + ":sphinx_module_tests", + ":test_safety_analysis_tools", + ":unit_component_tests", + ], +) diff --git a/bazel/rules/rules_score/test/test/fixtures/mock_lib1.cc b/bazel/rules/rules_score/test/test/fixtures/mock_lib1.cc new file mode 100644 index 00000000..599e3c18 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/mock_lib1.cc @@ -0,0 +1,4 @@ +// Mock implementation for testing purposes +int mock_function_1() { + return 42; +} diff --git a/bazel/rules/rules_score/test/test/fixtures/mock_lib2.cc b/bazel/rules/rules_score/test/test/fixtures/mock_lib2.cc new file mode 100644 index 00000000..588f15da --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/mock_lib2.cc @@ -0,0 +1,4 @@ +// Mock implementation for testing purposes +int mock_function_2() { + return 84; +} diff --git a/bazel/rules/rules_score/test/test/fixtures/mock_test.sh b/bazel/rules/rules_score/test/test/fixtures/mock_test.sh new file mode 100755 index 00000000..d5aa21e6 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/mock_test.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* + +# Mock test script that always succeeds +# Used for testing rule structure without actual implementation + +exit 0 diff --git a/bazel/rules/rules_score/test/test/fixtures/module_a/index.rst b/bazel/rules/rules_score/test/test/fixtures/module_a/index.rst new file mode 100644 index 00000000..573ad4bd --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/module_a/index.rst @@ -0,0 +1,31 @@ +Module A Documentation +====================== + +This is the documentation for Module A. + +.. document:: Documentation for Module A + :id: doc__module_fixtures_module_a + :status: valid + :safety: ASIL_B + :security: NO + :realizes: wp__component_arch + +Overview +-------- + +Module A is a simple module that depends on Module C. + +Features +-------- + +.. needlist:: + :tags: module_a + +Cross-Module References +----------------------- + +General reference to Module C :external+module_c_lib:doc:`index`. + +Need reference to Module C :need:`doc__module_fixtures_module_c`. + +Need reference to Module B :need:`doc__module_fixtures_module_b`. diff --git a/bazel/rules/rules_score/test/test/fixtures/module_b/index.rst b/bazel/rules/rules_score/test/test/fixtures/module_b/index.rst new file mode 100644 index 00000000..3155c10d --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/module_b/index.rst @@ -0,0 +1,37 @@ +Module B Documentation +====================== + +This is the documentation for Module B. + +.. document:: Documentation for Module B + :id: doc__module_fixtures_module_b + :status: valid + :safety: ASIL_B + :security: NO + :realizes: + +Overview +-------- + +Module B depends on both Module A and Module C. + +Features +-------- + +.. needlist:: + :tags: module_b + +Cross-Module References +----------------------- + +This module references: + +* :external+module_a_lib:doc:`index` from Module A +* :external+module_c_lib:doc:`index` from Module C +* Need reference to Module C :need:`doc__module_fixtures_module_c` +* Need reference to Module C :need:`doc__module_fixtures_module_d` + +Dependencies +------------ + +Module B integrates functionality from both dependent modules. diff --git a/bazel/rules/rules_score/test/test/fixtures/module_c/index.rst b/bazel/rules/rules_score/test/test/fixtures/module_c/index.rst new file mode 100644 index 00000000..b73ae61f --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/module_c/index.rst @@ -0,0 +1,29 @@ +Module C Documentation +====================== + +This is the documentation for Module C. + +.. document:: Documentation for Module C + :id: doc__module_fixtures_module_c + :status: valid + :safety: ASIL_B + :security: NO + :realizes: + + +Overview +-------- + +Module C is a base module with no dependencies. +Local need link: :need:`doc__module_fixtures_module_c` + +Features +-------- + +.. needlist:: + :tags: module_c + +Content +------- + +Module C provides foundational functionality used by other modules. diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/architectural_design.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/architectural_design.rst new file mode 100644 index 00000000..02e96f7d --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/architectural_design.rst @@ -0,0 +1,174 @@ +Architectural Design +==================== + +This document describes the architectural design of the test SEooC module. + +Software Architecture Overview +------------------------------- + +The system consists of the following software components: + +.. comp_arc_sta:: Input Processing Module + :id: comp_arc_sta__seooc_test__input_processing_module + :status: valid + :tags: architecture, component, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__input_data_processing, comp_req__seooc_test__can_message_reception + + Responsible for receiving and validating input data from CAN interface. + + **Inputs**: Raw CAN messages + + **Outputs**: Validated data structures + + **Safety Mechanisms**: CRC validation, sequence counter check + +.. comp_arc_sta:: Data Processing Engine + :id: comp_arc_sta__seooc_test__data_processing_engine + :status: valid + :tags: architecture, component, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__output_accuracy, comp_req__seooc_test__redundant_calculation + + Core processing component that performs calculations on validated data. + + **Inputs**: Validated data from Input Processing Module + + **Outputs**: Processed results + + **Safety Mechanisms**: Dual-channel redundant calculation + +.. comp_arc_sta:: Output Handler + :id: comp_arc_sta__seooc_test__output_handler + :status: valid + :tags: architecture, component, seooc_test + :safety: QM + :security: NO + :fulfils: comp_req__seooc_test__can_message_transmission + + Formats and transmits output data via CAN interface. + + **Inputs**: Processed results from Data Processing Engine + + **Outputs**: CAN messages + + **Safety Mechanisms**: Message sequence numbering, alive counter + +.. comp_arc_sta:: Fault Detection and Handling + :id: comp_arc_sta__seooc_test__fault_detection_handling + :status: valid + :tags: architecture, component, safety, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__fault_detection, comp_req__seooc_test__safe_state_transition + + Monitors system health and handles fault conditions. + + **Inputs**: Status from all components + + **Outputs**: System state, error flags + + **Safety Mechanisms**: Watchdog timer, plausibility checks + +Component Interfaces +--------------------- + +Interface: CAN Communication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. real_arc_int:: CAN RX Interface + :id: real_arc_int__seooc_test__can_rx + :status: valid + :tags: interface, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__can_message_reception + :language: cpp + + * **Protocol**: CAN 2.0B + * **Baud Rate**: 500 kbps + * **Message ID Range**: 0x100-0x1FF + * **DLC**: 8 bytes + +.. real_arc_int:: CAN TX Interface + :id: real_arc_int__seooc_test__can_tx + :status: valid + :tags: interface, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__can_message_transmission + :language: cpp + + * **Protocol**: CAN 2.0B + * **Baud Rate**: 500 kbps + * **Message ID Range**: 0x200-0x2FF + * **DLC**: 8 bytes + +Design Decisions +---------------- + +.. comp_arc_dyn:: Use of Hardware Watchdog + :id: comp_arc_dyn__seooc_test__hw_watchdog + :status: valid + :tags: design-decision, safety, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__fault_detection + + The architecture includes a hardware watchdog timer to ensure system + reliability and meet safety requirements. + + **Rationale**: Hardware watchdog provides independent monitoring + of software execution and can detect timing violations. + + **Alternatives Considered**: Software-only monitoring (rejected due + to lower ASIL coverage) + +.. comp_arc_dyn:: Redundant Processing Paths + :id: comp_arc_dyn__seooc_test__redundancy + :status: valid + :tags: design-decision, safety, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__redundant_calculation + + Critical calculations are performed using redundant processing paths + to detect and prevent silent data corruption. + + **Rationale**: Meets ASIL-B requirements for detection of random + hardware faults during calculation. + + **Implementation**: Main path + shadow path with result comparison + +Memory Architecture +------------------- + +.. comp_arc_sta:: RAM Allocation + :id: comp_arc_sta__seooc_test__ram_allocation + :status: valid + :tags: resource, memory, seooc_test + :safety: QM + :security: NO + :fulfils: aou_req__seooc_test__memory_requirements + + * **Total RAM**: 512 KB + * **Stack**: 64 KB + * **Heap**: 128 KB + * **Static Data**: 256 KB + * **Reserved**: 64 KB + +.. comp_arc_sta:: Flash Allocation + :id: comp_arc_sta__seooc_test__flash_allocation + :status: valid + :tags: resource, memory, seooc_test + :safety: QM + :security: NO + :fulfils: aou_req__seooc_test__memory_requirements + + * **Total Flash**: 2 MB + * **Application Code**: 1.5 MB + * **Configuration Data**: 256 KB + * **Boot Loader**: 128 KB + * **Reserved**: 128 KB diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/asr_fixtures.trlc b/bazel/rules/rules_score/test/test/fixtures/seooc_test/asr_fixtures.trlc new file mode 100644 index 00000000..2a08c183 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/asr_fixtures.trlc @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SeoocTest + +import ScoreReq + +ScoreReq.AssumedSystemReq ASR_001 { + description = "The system shall process data" + safety = ScoreReq.Asil.QM + rationale = "Required for basic functionality" + version = 1 +} diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/assumptions_of_use.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/assumptions_of_use.rst new file mode 100644 index 00000000..fae172cd --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/assumptions_of_use.rst @@ -0,0 +1,80 @@ +Assumptions of Use +================== + +This document describes the assumptions of use for the test SEooC module. + +.. aou_req:: Operating Temperature Range + :id: aou_req__seooc_test__operating_temperature_range + :status: valid + :tags: environment, iso26262, seooc_test + :safety: ASIL_B + :security: NO + + The SEooC shall operate within temperature range -40°C to +85°C. + +.. aou_req:: Supply Voltage + :id: aou_req__seooc_test__supply_voltage + :status: valid + :tags: power, iso26262, seooc_test + :safety: ASIL_B + :security: NO + + The SEooC shall operate with supply voltage 12V ±10%. + + Maximum current consumption: 2.5A + +.. aou_req:: Processing Load + :id: aou_req__seooc_test__processing_load + :status: valid + :tags: performance, iso26262, seooc_test + :safety: ASIL_B + :security: NO + + The maximum processing load shall not exceed 80% to ensure + timing requirements are met. + +Environmental Assumptions +------------------------- + +.. aou_req:: Controlled Environment + :id: aou_req__seooc_test__controlled_environment + :status: valid + :tags: environment, seooc_test + :safety: ASIL_B + :security: NO + + The system operates in a controlled automotive environment + compliant with ISO 16750 standards. + +.. aou_req:: Maintenance + :id: aou_req__seooc_test__maintenance + :status: valid + :tags: maintenance, seooc_test + :safety: ASIL_B + :security: NO + + Regular maintenance is performed according to the maintenance + schedule defined in the integration manual. + +Integration Constraints +----------------------- + +.. aou_req:: CAN Bus Interface + :id: aou_req__seooc_test__can_bus_interface + :status: valid + :tags: interface, communication, seooc_test + :safety: ASIL_B + :security: NO + + The host system shall provide a CAN 2.0B compliant interface + for communication with the SEooC. + +.. aou_req:: Memory Requirements + :id: aou_req__seooc_test__memory_requirements + :status: valid + :tags: resource, seooc_test + :safety: ASIL_B + :security: NO + + The host system shall provide at least 512KB of RAM and + 2MB of flash memory for the SEooC. diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/comp_req_fixtures.trlc b/bazel/rules/rules_score/test/test/fixtures/seooc_test/comp_req_fixtures.trlc new file mode 100644 index 00000000..97be39b5 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/comp_req_fixtures.trlc @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SeoocTest + +import ScoreReq + +ScoreReq.CompReq COMP_001 { + description = "The component shall process input data within defined latency bounds" + safety = ScoreReq.Asil.QM + derived_from = [SeoocTest.FEAT_001@1] + version = 1 +} diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/component_requirements.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/component_requirements.rst new file mode 100644 index 00000000..1d7f90c2 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/component_requirements.rst @@ -0,0 +1,105 @@ +Component Requirements +====================== + +This document defines the functional and safety requirements. + +Functional Requirements +------------------------ + +.. comp_req:: Input Data Processing + :id: comp_req__seooc_test__input_data_processing + :status: valid + :tags: functional, performance, seooc_test + :safety: QM + :security: NO + :satisfies: aou_req__seooc_test__processing_load + + The system shall process input data within 100ms from reception. + + **Rationale**: Real-time processing required for control loop. + +.. comp_req:: Output Accuracy + :id: comp_req__seooc_test__output_accuracy + :status: valid + :tags: functional, quality, seooc_test + :safety: QM + :security: NO + + The system shall provide output with 99.9% accuracy under + nominal operating conditions. + +.. comp_req:: Data Logging + :id: comp_req__seooc_test__data_logging + :status: valid + :tags: functional, diagnostic, seooc_test + :safety: QM + :security: NO + + The system shall log all error events with timestamp and + error code to non-volatile memory. + +Safety Requirements +------------------- + +.. comp_req:: Fault Detection + :id: comp_req__seooc_test__fault_detection + :status: valid + :tags: safety, seooc_test + :safety: ASIL_B + :security: NO + :satisfies: aou_req__seooc_test__processing_load + + The system shall detect and handle fault conditions within 50ms. + + **ASIL Level**: ASIL-B + **Safety Mechanism**: Watchdog timer + plausibility checks + +.. comp_req:: Safe State Transition + :id: comp_req__seooc_test__safe_state_transition + :status: valid + :tags: safety, seooc_test + :safety: ASIL_B + :security: NO + + The system shall maintain safe state during power loss and + complete shutdown within 20ms. + + **ASIL Level**: ASIL-B + **Safe State**: All outputs disabled, error flag set + +.. comp_req:: Redundant Calculation + :id: comp_req__seooc_test__redundant_calculation + :status: valid + :tags: safety, seooc_test + :safety: ASIL_B + :security: NO + + Critical calculations shall be performed using redundant + processing paths with comparison. + + **ASIL Level**: ASIL-B + **Safety Mechanism**: Dual-channel processing + +Communication Requirements +--------------------------- + +.. comp_req:: CAN Message Transmission + :id: comp_req__seooc_test__can_message_transmission + :status: valid + :tags: functional, communication, seooc_test + :safety: QM + :security: NO + :satisfies: aou_req__seooc_test__can_bus_interface + + The system shall transmit status messages on CAN bus + every 100ms ±10ms. + +.. comp_req:: CAN Message Reception + :id: comp_req__seooc_test__can_message_reception + :status: valid + :tags: functional, communication, seooc_test + :safety: QM + :security: NO + :satisfies: aou_req__seooc_test__can_bus_interface + + The system shall process received CAN messages within 10ms. diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/dependability_analysis.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/dependability_analysis.rst new file mode 100644 index 00000000..ea5b518e --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/dependability_analysis.rst @@ -0,0 +1,292 @@ +Safety Analysis +=============== + +This document contains the safety analysis for the test SEooC module. + +Failure Mode and Effects Analysis (FMEA) +----------------------------------------- + +.. comp_saf_fmea:: Input Data Corruption + :id: comp_saf_fmea__seooc_test__input_data_corruption + :status: valid + :tags: fmea, safety, seooc_test + :violates: comp_arc_sta__seooc_test__input_processing_module + :fault_id: bit_flip + :failure_effect: Corrupted input data from CAN bus due to electromagnetic interference, transmission errors, or faulty sensor leading to incorrect processing results + :mitigated_by: comp_req__seooc_test__fault_detection + :sufficient: yes + + **Failure Mode**: Corrupted input data from CAN bus + + **Potential Causes**: + + * Electromagnetic interference + * Transmission errors + * Faulty sensor + + **Effects**: Incorrect processing results, potential unsafe output + + **Severity**: High (S9) + + **Occurrence**: Medium (O4) + + **Detection**: High (D2) + + **RPN**: 72 + + **Detection Method**: CRC checksum validation, sequence counter check + + **Mitigation**: Reject invalid data and enter safe state within 50ms + +.. comp_saf_fmea:: Processing Timeout + :id: comp_saf_fmea__seooc_test__processing_timeout + :status: valid + :tags: fmea, safety, seooc_test + :violates: comp_arc_sta__seooc_test__fault_detection_handling + :fault_id: timing_failure + :failure_effect: Processing exceeds time deadline due to software defect, CPU overload, or hardware fault causing system unresponsiveness + :mitigated_by: comp_req__seooc_test__fault_detection + :sufficient: yes + + **Failure Mode**: Processing exceeds time deadline + + **Potential Causes**: + + * Software defect (infinite loop) + * CPU overload + * Hardware fault + + **Effects**: System becomes unresponsive, watchdog reset + + **Severity**: Medium (S6) + + **Occurrence**: Low (O3) + + **Detection**: Very High (D1) + + **RPN**: 18 + + **Detection Method**: Hardware watchdog timer + + **Mitigation**: System reset and recovery to safe state + +.. comp_saf_fmea:: Calculation Error + :id: comp_saf_fmea__seooc_test__calculation_error + :status: valid + :tags: fmea, safety, seooc_test + :violates: comp_arc_sta__seooc_test__data_processing_engine + :fault_id: seu + :failure_effect: Incorrect calculation result due to single event upset, register corruption, or ALU malfunction + :mitigated_by: comp_req__seooc_test__redundant_calculation + :sufficient: yes + + **Failure Mode**: Incorrect calculation result due to random hardware fault + + **Potential Causes**: + + * Single event upset (SEU) + * Register corruption + * ALU malfunction + + **Effects**: Incorrect output values + + **Severity**: High (S8) + + **Occurrence**: Very Low (O2) + + **Detection**: High (D2) + + **RPN**: 32 + + **Detection Method**: Dual-channel redundant calculation with comparison + + **Mitigation**: Discard result and use previous valid value, set error flag + +Dependent Failure Analysis (DFA) +--------------------------------- + +.. comp_saf_dfa:: System Failure Top Event + :id: comp_saf_dfa__seooc_test__system_failure_top + :status: valid + :tags: dfa, safety, seooc_test + :violates: comp_arc_sta__seooc_test__data_processing_engine + :failure_id: common_cause + :failure_effect: System provides unsafe output due to common cause failures affecting multiple safety mechanisms simultaneously + :mitigated_by: aou_req__seooc_test__controlled_environment + :sufficient: yes + + **Top Event**: System provides unsafe output + + **Goal**: Probability < 1e-6 per hour (ASIL-B target) + +.. comp_saf_dfa:: Hardware Failure Branch + :id: comp_saf_dfa__seooc_test__hw_failure + :status: valid + :tags: dfa, safety, seooc_test + :violates: comp_arc_sta__seooc_test__data_processing_engine + :failure_id: hw_common_mode + :failure_effect: Hardware component failures due to common cause (overvoltage, overtemperature) affecting multiple components + :mitigated_by: aou_req__seooc_test__operating_temperature_range, aou_req__seooc_test__supply_voltage + :sufficient: yes + + **Event**: Hardware component failure + + **Sub-events**: + + * Microcontroller failure (λ = 5e-7) + * Power supply failure (λ = 3e-7) + * CAN transceiver failure (λ = 2e-7) + + **Combined Probability**: 1.0e-6 per hour + +.. comp_saf_dfa:: Software Failure Branch + :id: comp_saf_dfa__seooc_test__sw_failure + :status: valid + :tags: dfa, safety, seooc_test + :violates: comp_arc_sta__seooc_test__data_processing_engine + :failure_id: sw_systematic + :failure_effect: Software defect affecting both processing channels due to systematic fault in common code base + :mitigated_by: comp_req__seooc_test__redundant_calculation + :sufficient: yes + + **Event**: Software defect leads to unsafe output + + **Sub-events**: + + * Undetected software bug (λ = 8e-6, detection coverage 90%) + * Memory corruption (λ = 1e-7) + + **Combined Probability**: 9e-7 per hour (after detection coverage) + +.. comp_saf_dfa:: External Interference Branch + :id: comp_saf_dfa__seooc_test__ext_interference + :status: valid + :tags: dfa, safety, seooc_test + :violates: comp_arc_sta__seooc_test__input_processing_module + :failure_id: emi + :failure_effect: External interference causing simultaneous malfunction of multiple components + :mitigated_by: aou_req__seooc_test__controlled_environment + :sufficient: yes + + **Event**: External interference causes malfunction + + **Sub-events**: + + * EMI beyond specification (λ = 5e-8) + * Voltage transient (λ = 2e-8, mitigation 99%) + + **Combined Probability**: 5.2e-8 per hour (after mitigation) + +**Total System Failure Probability**: 1.95e-6 per hour + +**ASIL-B Target**: < 1e-5 per hour ✓ **PASSED** + +Safety Mechanisms +----------------- + +.. comp_arc_sta:: SM: Input Validation + :id: comp_arc_sta__seooc_test__sm_input_validation + :status: valid + :tags: safety-mechanism, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__fault_detection + + **Description**: All input data is validated before processing + + **Checks Performed**: + + * CRC-16 checksum validation + * Message sequence counter verification + * Data range plausibility checks + + **Diagnostic Coverage**: 95% + + **Reaction**: Reject invalid data, increment error counter, use last valid value + +.. comp_arc_sta:: SM: Watchdog Timer + :id: comp_arc_sta__seooc_test__sm_watchdog + :status: valid + :tags: safety-mechanism, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__fault_detection + + **Description**: Hardware watchdog monitors software execution + + **Configuration**: + + * Timeout: 150ms + * Window watchdog: 100-140ms trigger window + * Reset delay: 10ms + + **Diagnostic Coverage**: 99% + + **Reaction**: System reset, boot to safe state + +.. comp_arc_sta:: SM: Redundant Calculation + :id: comp_arc_sta__seooc_test__sm_redundant_calc + :status: valid + :tags: safety-mechanism, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__redundant_calculation + + **Description**: Critical calculations performed in dual channels + + **Implementation**: + + * Main calculation path + * Independent shadow path + * Result comparison with tolerance check + + **Diagnostic Coverage**: 98% + + **Reaction**: On mismatch, use previous valid value, set error flag + +Safety Validation Results +-------------------------- + +.. comp_arc_dyn:: Validation: FMEA Coverage + :id: comp_arc_dyn__seooc_test__val_fmea_coverage + :status: valid + :tags: validation, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__fault_detection + + **Result**: All identified failure modes have detection mechanisms + + **Coverage**: 100% of critical failure modes + + **Status**: ✓ PASSED + +.. comp_arc_dyn:: Validation: DFA Target Achievement + :id: comp_arc_dyn__seooc_test__val_dfa_target + :status: valid + :tags: validation, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__safe_state_transition + + **Result**: System failure probability 1.95e-6 per hour + + **Target**: < 1e-5 per hour (ASIL-B) + + **Margin**: 5.1x + + **Status**: ✓ PASSED + +.. comp_arc_dyn:: Validation: Safety Mechanism Effectiveness + :id: comp_arc_dyn__seooc_test__val_sm_effectiveness + :status: valid + :tags: validation, seooc_test + :safety: ASIL_B + :security: NO + :fulfils: comp_req__seooc_test__redundant_calculation + + **Result**: Combined diagnostic coverage 97.3% + + **Target**: > 90% (ASIL-B) + + **Status**: ✓ PASSED diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/dfa.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/dfa.rst new file mode 100644 index 00000000..7b2e30d9 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/dfa.rst @@ -0,0 +1,149 @@ +.. + # ******************************************************************************* + # Copyright (c) 2025 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 + # ******************************************************************************* + +Dependent Failure Analysis (DFA) +================================ + +This document contains the Dependent Failure Analysis (DFA) for the test SEooC module, +following ISO 26262 requirements for analysis of dependent failures. + +Component DFA Overview +---------------------- + +The dependent failure analysis identifies and evaluates common cause failures, +cascading failures, and dependent failures that could affect the safety of the component. + +.. comp_saf_dfa:: Common Cause Failure Analysis + :id: comp_saf_dfa__seooc_test__common_cause_analysis + :status: valid + :tags: dfa, safety, seooc_test + :violates: comp_arc_sta__seooc_test__data_processing_engine + :failure_id: ccf_root + :failure_effect: Common cause failures affecting multiple safety mechanisms simultaneously + :mitigated_by: aou_req__seooc_test__controlled_environment + :sufficient: yes + + **Analysis Scope**: Identification of common cause failures + + **Initiators Analyzed**: + + * Environmental conditions (temperature, EMI, vibration) + * Power supply anomalies + * Manufacturing and design defects + * Maintenance-induced failures + + **Conclusion**: All identified common cause initiators have adequate mitigation measures. + +.. comp_saf_dfa:: Power Supply Dependency + :id: comp_saf_dfa__seooc_test__power_dependency + :status: valid + :tags: dfa, safety, seooc_test + :violates: comp_arc_sta__seooc_test__data_processing_engine + :failure_id: power_ccf + :failure_effect: Power supply failure affecting both main and redundant processing paths + :mitigated_by: aou_req__seooc_test__supply_voltage + :sufficient: yes + + **Dependent Failure**: Power supply failure + + **Affected Elements**: + + * Main processing unit + * Redundant calculation path + * Communication interface + + **Independence Measures**: + + * Voltage monitoring with independent reference + * Brownout detection circuit + * Defined safe state on power loss + + **Residual Risk**: Acceptable (< 1e-8 per hour) + +.. comp_saf_dfa:: Clock Source Dependency + :id: comp_saf_dfa__seooc_test__clock_dependency + :status: valid + :tags: dfa, safety, seooc_test + :violates: comp_arc_sta__seooc_test__data_processing_engine + :failure_id: clock_ccf + :failure_effect: Clock failure causing simultaneous malfunction of timing-dependent safety mechanisms + :mitigated_by: comp_req__seooc_test__fault_detection + :sufficient: yes + + **Dependent Failure**: Clock source failure + + **Affected Elements**: + + * Watchdog timer + * Communication timing + * Task scheduling + + **Independence Measures**: + + * Internal RC oscillator as backup + * Clock monitoring unit + * Frequency range checks + + **Residual Risk**: Acceptable (< 5e-9 per hour) + +.. comp_saf_dfa:: Software Design Dependency + :id: comp_saf_dfa__seooc_test__sw_design_dependency + :status: valid + :tags: dfa, safety, seooc_test + :violates: comp_arc_sta__seooc_test__data_processing_engine + :failure_id: sw_ccf + :failure_effect: Systematic software defect in common code base affecting both calculation paths + :mitigated_by: comp_req__seooc_test__redundant_calculation + :sufficient: yes + + **Dependent Failure**: Systematic software defect + + **Affected Elements**: + + * Main calculation algorithm + * Redundant calculation algorithm + * Result comparison logic + + **Independence Measures**: + + * Diverse implementation of redundant path + * Independent development teams + * Different compilers/toolchains for each path + + **Residual Risk**: Acceptable (< 1e-7 per hour with diversity measures) + +DFA Summary +----------- + +.. comp_saf_dfa:: DFA Summary and Conclusion + :id: comp_saf_dfa__seooc_test__dfa_summary + :status: valid + :tags: dfa, safety, seooc_test, summary + :violates: comp_arc_sta__seooc_test__data_processing_engine + :failure_id: dfa_summary + :failure_effect: Combined dependent failure probability assessment + :mitigated_by: aou_req__seooc_test__controlled_environment + :sufficient: yes + + **Total Dependent Failure Probability**: < 1.5e-7 per hour + + **ASIL-B Target for Dependent Failures**: < 1e-6 per hour + + **Margin**: 6.7x + + **Status**: ✓ PASSED + + **Conclusion**: The component design provides adequate independence between + safety mechanisms. All identified dependent failure modes have been analyzed + and appropriate mitigation measures are in place. diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/dynamic_architecture.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/dynamic_architecture.rst new file mode 100644 index 00000000..33cf03f5 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/dynamic_architecture.rst @@ -0,0 +1,66 @@ +.. + # ******************************************************************************* + # Copyright (c) 2025 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 + # ******************************************************************************* + +Dynamic Architecture +==================== + +This file contains the dynamic architectural design for the SEooC test component. + +.. comp_arc_dyn:: Data Processing Sequence + :id: comp_arc_dyn__seooc_test__data_processing + :security: NO + :safety: QM + :status: valid + :fulfils: comp_req__seooc_test__input_data_processing + + Sequence diagram showing the data processing flow from input to output. + + .. uml:: + + @startuml + participant "Client" as client + participant "SEooC Test Component" as main + participant "Data Processor" as processor + + client -> main : processData(input) + main -> processor : process(input) + processor --> main : result + main --> client : output + @enduml + +.. comp_arc_dyn:: Fault Handling Sequence + :id: comp_arc_dyn__seooc_test__fault_handling + :security: NO + :safety: ASIL_B + :status: valid + :fulfils: comp_req__seooc_test__fault_detection + + Sequence diagram showing the fault detection and safe state transition. + + .. uml:: + + @startuml + participant "Main Component" as main + participant "Fault Handler" as fault + participant "Safe State Manager" as safe + + main -> fault : checkHealth() + alt fault detected + fault -> safe : transitionToSafeState() + safe --> fault : safeStateConfirmed + fault --> main : faultHandled + else no fault + fault --> main : healthOK + end + @enduml diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/feat_req_fixtures.trlc b/bazel/rules/rules_score/test/test/fixtures/seooc_test/feat_req_fixtures.trlc new file mode 100644 index 00000000..a9b85aef --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/feat_req_fixtures.trlc @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SeoocTest + +import ScoreReq + +ScoreReq.FeatReq FEAT_001 { + description = "The component shall process input data" + safety = ScoreReq.Asil.QM + derived_from = [SeoocTest.ASR_001@1] + version = 1 +} diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/feature_requirements.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/feature_requirements.rst new file mode 100644 index 00000000..d1be18a5 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/feature_requirements.rst @@ -0,0 +1,48 @@ +.. + # ******************************************************************************* + # Copyright (c) 2025 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 + # ******************************************************************************* + +Feature Requirements +==================== + +This file contains the feature requirements for the SEooC test module. + +.. feat_req:: Data Processing + :id: feat_req__seooc_test__data_processing + :reqtype: Functional + :security: NO + :safety: QM + :satisfies: stkh_req__platform__data_handling + :status: valid + + The SEooC test component shall process input data and provide processed output. + +.. feat_req:: Safe State Management + :id: feat_req__seooc_test__safe_state + :reqtype: Functional + :security: NO + :safety: ASIL_B + :satisfies: stkh_req__platform__safety + :status: valid + + The SEooC test component shall transition to a safe state upon detection of a fault condition. + +.. feat_req:: CAN Communication + :id: feat_req__seooc_test__can_comm + :reqtype: Interface + :security: NO + :safety: QM + :satisfies: stkh_req__platform__communication + :status: valid + + The SEooC test component shall support CAN message transmission and reception. diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/static_architecture.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/static_architecture.rst new file mode 100644 index 00000000..b81321cb --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/seooc_test/static_architecture.rst @@ -0,0 +1,45 @@ +.. + # ******************************************************************************* + # Copyright (c) 2025 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 + # ******************************************************************************* + +Static Architecture +=================== + +This file contains the static architectural design for the SEooC test component. + +.. comp_arc_sta:: SEooC Test Component + :id: comp_arc_sta__seooc_test__main + :security: NO + :safety: QM + :status: valid + :fulfils: comp_req__seooc_test__input_data_processing + + The main component of the SEooC test module providing data processing capabilities. + +.. comp_arc_sta:: Data Processor + :id: comp_arc_sta__seooc_test__data_processor + :security: NO + :safety: QM + :status: valid + :fulfils: comp_req__seooc_test__output_accuracy + + Sub-component responsible for processing input data and generating output. + +.. comp_arc_sta:: Fault Handler + :id: comp_arc_sta__seooc_test__fault_handler + :security: NO + :safety: ASIL_B + :status: valid + :fulfils: comp_req__seooc_test__fault_detection + + Sub-component responsible for detecting and handling fault conditions. diff --git a/bazel/rules/rules_score/test/test/fixtures/test_binary_unit_test.cc b/bazel/rules/rules_score/test/test/fixtures/test_binary_unit_test.cc new file mode 100644 index 00000000..c6db43f3 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/test_binary_unit_test.cc @@ -0,0 +1,16 @@ +// Unit tests for test_binary_unit fixture +#include + +// Declarations from mock libraries +extern int mock_function_1(); +extern int mock_function_2(); + +TEST(BinaryUnitTest, MockFunction1ReturnsExpectedValue) { + ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); + EXPECT_EQ(mock_function_1(), 42); +} + +TEST(BinaryUnitTest, MockFunction2ReturnsExpectedValue) { + ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); + EXPECT_EQ(mock_function_2(), 84); +} diff --git a/bazel/rules/rules_score/test/test/fixtures/test_component_main.cc b/bazel/rules/rules_score/test/test/fixtures/test_component_main.cc new file mode 100644 index 00000000..578f2d59 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/test_component_main.cc @@ -0,0 +1,13 @@ +// Main implementation for test_component +#include + +// Declarations from mock libraries +extern int mock_function_1(); +extern int mock_function_2(); + +int main(int argc, char** argv) { + std::cout << "Test Component Implementation" << std::endl; + std::cout << "Mock function 1 returns: " << mock_function_1() << std::endl; + std::cout << "Mock function 2 returns: " << mock_function_2() << std::endl; + return 0; +} diff --git a/bazel/rules/rules_score/test/test/fixtures/test_unit2_test.cc b/bazel/rules/rules_score/test/test/fixtures/test_unit2_test.cc new file mode 100644 index 00000000..5c6e3594 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/test_unit2_test.cc @@ -0,0 +1,16 @@ +// Unit tests for test_unit2 fixture +#include + +// Declarations from mock libraries +extern int mock_function_1(); +extern int mock_function_2(); + +TEST(Unit2Test, MockFunction1ReturnsExpectedValue) { + ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); + EXPECT_EQ(mock_function_1(), 42); +} + +TEST(Unit2Test, MockFunction2ReturnsExpectedValue) { + ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); + EXPECT_EQ(mock_function_2(), 84); +} diff --git a/bazel/rules/rules_score/test/test/fixtures/test_unit_test.cc b/bazel/rules/rules_score/test/test/fixtures/test_unit_test.cc new file mode 100644 index 00000000..2bf34a57 --- /dev/null +++ b/bazel/rules/rules_score/test/test/fixtures/test_unit_test.cc @@ -0,0 +1,16 @@ +// Unit tests for mock libraries +#include + +// Declarations from mock libraries +extern int mock_function_1(); +extern int mock_function_2(); + +TEST(MockLibTest, MockFunction1ReturnsExpectedValue) { + ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); + EXPECT_EQ(mock_function_1(), 42); +} + +TEST(MockLibTest, MockFunction2ReturnsExpectedValue) { + ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); + EXPECT_EQ(mock_function_2(), 84); +} diff --git a/bazel/rules/rules_score/test/test/html_generation_test.bzl b/bazel/rules/rules_score/test/test/html_generation_test.bzl new file mode 100644 index 00000000..788a940f --- /dev/null +++ b/bazel/rules/rules_score/test/test/html_generation_test.bzl @@ -0,0 +1,223 @@ +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* +"""Test rules for sphinx_module HTML generation and dependencies.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("//bazel/rules/rules_score:providers.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") + +# ============================================================================ +# Provider Tests +# ============================================================================ + +def _providers_test_impl(ctx): + """Test that sphinx_module provides the correct providers.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Verify required providers + asserts.true( + env, + SphinxModuleInfo in target_under_test, + "Target should provide SphinxModuleInfo", + ) + + asserts.true( + env, + DefaultInfo in target_under_test, + "Target should provide DefaultInfo", + ) + + return analysistest.end(env) + +providers_test = analysistest.make(_providers_test_impl) + +# ============================================================================ +# HTML Generation Tests +# ============================================================================ + +def _basic_html_generation_test_impl(ctx): + """Test that a simple document generates HTML output.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Check that HTML directory exists + score_info = target_under_test[SphinxModuleInfo] + asserts.true( + env, + score_info.html_dir != None, + "Module should generate HTML directory", + ) + + return analysistest.end(env) + +basic_html_generation_test = analysistest.make(_basic_html_generation_test_impl) + +# ============================================================================ +# Needs.json Generation Tests +# ============================================================================ + +def _needs_generation_test_impl(ctx): + """Test that sphinx_module generates needs.json files.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Check for SphinxNeedsInfo provider on _needs target + # Note: This test requires the _needs suffix target + asserts.true( + env, + DefaultInfo in target_under_test, + "Needs target should provide DefaultInfo", + ) + + return analysistest.end(env) + +needs_generation_test = analysistest.make(_needs_generation_test_impl) + +def _needs_transitive_test_impl(ctx): + """Test that needs.json files are collected transitively.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Verify SphinxNeedsInfo provider + asserts.true( + env, + SphinxNeedsInfo in target_under_test, + "Needs target should provide SphinxNeedsInfo", + ) + + needs_info = target_under_test[SphinxNeedsInfo] + + # Check direct needs.json file + asserts.true( + env, + needs_info.needs_json_file != None, + "Should have direct needs.json file", + ) + + # Check transitive needs collection + asserts.true( + env, + needs_info.needs_json_files != None, + "Should have transitive needs.json files depset", + ) + + return analysistest.end(env) + +needs_transitive_test = analysistest.make(_needs_transitive_test_impl) + +# ============================================================================ +# Dependency and Integration Tests +# ============================================================================ + +def _module_dependencies_test_impl(ctx): + """Test that module dependencies are properly handled.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + score_info = target_under_test[SphinxModuleInfo] + + # Module with dependencies should still generate HTML + asserts.true( + env, + score_info.html_dir != None, + "Module with dependencies should generate HTML", + ) + + return analysistest.end(env) + +module_dependencies_test = analysistest.make(_module_dependencies_test_impl) + +def _html_merging_test_impl(ctx): + """Test that HTML from dependencies is merged correctly.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + score_info = target_under_test[SphinxModuleInfo] + + # Verify merged HTML output exists + asserts.true( + env, + score_info.html_dir != None, + "Merged HTML should be generated", + ) + + return analysistest.end(env) + +html_merging_test = analysistest.make(_html_merging_test_impl) + +# ============================================================================ +# Config Generation Tests +# ============================================================================ + +def _auto_config_generation_test_impl(ctx): + """Test that conf.py is automatically generated when not provided.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + score_info = target_under_test[SphinxModuleInfo] + + # Module without explicit config should still generate HTML + asserts.true( + env, + score_info.html_dir != None, + "Module with auto-generated config should produce HTML", + ) + + return analysistest.end(env) + +auto_config_generation_test = analysistest.make(_auto_config_generation_test_impl) + +def _explicit_config_test_impl(ctx): + """Test that explicit conf.py is used when provided.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + score_info = target_under_test[SphinxModuleInfo] + + # Module with explicit config should generate HTML + asserts.true( + env, + score_info.html_dir != None, + "Module with explicit config should produce HTML", + ) + + return analysistest.end(env) + +explicit_config_test = analysistest.make(_explicit_config_test_impl) + +# ============================================================================ +# Test Suite +# ============================================================================ + +def sphinx_module_test_suite(name): + """Create a comprehensive test suite for sphinx_module. + + Tests cover: + - Needs.json generation and transitive collection + - Module dependencies and HTML merging + + Args: + name: Name of the test suite + """ + + native.test_suite( + name = name, + tests = [ + # Needs generation + ":needs_transitive_test", + + # Dependencies and integration + ":module_dependencies_test", + ":html_merging_test", + ], + ) diff --git a/bazel/rules/rules_score/test/test/score_module_providers_test.bzl b/bazel/rules/rules_score/test/test/score_module_providers_test.bzl new file mode 100644 index 00000000..4dc9ead5 --- /dev/null +++ b/bazel/rules/rules_score/test/test/score_module_providers_test.bzl @@ -0,0 +1,323 @@ +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* +"""Tests for sphinx_module providers and two-phase build system.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("//bazel/rules/rules_score:providers.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") + +# ============================================================================ +# SphinxModuleInfo Provider Tests +# ============================================================================ + +def _sphinx_module_info_fields_test_impl(ctx): + """Test that SphinxModuleInfo provides all required fields.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + asserts.true( + env, + SphinxModuleInfo in target_under_test, + "Target should provide SphinxModuleInfo", + ) + + score_info = target_under_test[SphinxModuleInfo] + + # Verify html_dir field + asserts.true( + env, + hasattr(score_info, "html_dir"), + "SphinxModuleInfo should have html_dir field", + ) + + asserts.true( + env, + score_info.html_dir != None, + "html_dir should not be None", + ) + + return analysistest.end(env) + +sphinx_module_info_fields_test = analysistest.make(_sphinx_module_info_fields_test_impl) + +# ============================================================================ +# SphinxNeedsInfo Provider Tests +# ============================================================================ + +def _score_needs_info_fields_test_impl(ctx): + """Test that SphinxNeedsInfo provides all required fields.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + asserts.true( + env, + SphinxNeedsInfo in target_under_test, + "Needs target should provide SphinxNeedsInfo", + ) + + needs_info = target_under_test[SphinxNeedsInfo] + + # Verify needs_json_file field (direct file) + asserts.true( + env, + hasattr(needs_info, "needs_json_file"), + "SphinxNeedsInfo should have needs_json_file field", + ) + + asserts.true( + env, + needs_info.needs_json_file != None, + "needs_json_file should not be None", + ) + + # Verify needs_json_files field (transitive depset) + asserts.true( + env, + hasattr(needs_info, "needs_json_files"), + "SphinxNeedsInfo should have needs_json_files field", + ) + + asserts.true( + env, + needs_info.needs_json_files != None, + "needs_json_files should not be None", + ) + + # Verify it's a depset + asserts.true( + env, + type(needs_info.needs_json_files) == type(depset([])), + "needs_json_files should be a depset", + ) + + return analysistest.end(env) + +score_needs_info_fields_test = analysistest.make(_score_needs_info_fields_test_impl) + +def _score_needs_transitive_collection_test_impl(ctx): + """Test that needs.json files are collected transitively.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + needs_info = target_under_test[SphinxNeedsInfo] + + # Get the list of transitive needs files + transitive_needs = needs_info.needs_json_files.to_list() + + # Should have at least the direct needs file + asserts.true( + env, + len(transitive_needs) >= 1, + "Should have at least the direct needs.json file", + ) + + # Direct file should be in the transitive set + direct_file = needs_info.needs_json_file + asserts.true( + env, + direct_file in transitive_needs, + "Direct needs.json file should be in transitive collection", + ) + + return analysistest.end(env) + +score_needs_transitive_collection_test = analysistest.make(_score_needs_transitive_collection_test_impl) + +def _score_needs_with_deps_test_impl(ctx): + """Test that needs.json files include dependencies.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + needs_info = target_under_test[SphinxNeedsInfo] + transitive_needs = needs_info.needs_json_files.to_list() + + # Module with dependencies should have multiple needs files + # (its own + dependencies) + asserts.true( + env, + len(transitive_needs) >= 1, + "Module with dependencies should collect transitive needs.json files", + ) + + return analysistest.end(env) + +score_needs_with_deps_test = analysistest.make(_score_needs_with_deps_test_impl) + +# ============================================================================ +# Two-Phase Build Tests +# ============================================================================ + +def _two_phase_needs_first_test_impl(ctx): + """Test that Phase 1 (needs generation) works independently.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Verify SphinxNeedsInfo provider + asserts.true( + env, + SphinxNeedsInfo in target_under_test, + "Phase 1 should provide SphinxNeedsInfo", + ) + + # Verify DefaultInfo with needs.json output + asserts.true( + env, + DefaultInfo in target_under_test, + "Phase 1 should provide DefaultInfo", + ) + + default_info = target_under_test[DefaultInfo] + files = default_info.files.to_list() + + # Should have at least one file (needs.json) + asserts.true( + env, + len(files) >= 1, + "Phase 1 should output needs.json file", + ) + + return analysistest.end(env) + +two_phase_needs_first_test = analysistest.make(_two_phase_needs_first_test_impl) + +def _two_phase_html_second_test_impl(ctx): + """Test that Phase 2 (HTML generation) works with needs from Phase 1.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Verify SphinxModuleInfo provider + asserts.true( + env, + SphinxModuleInfo in target_under_test, + "Phase 2 should provide SphinxModuleInfo", + ) + + score_info = target_under_test[SphinxModuleInfo] + + # Verify HTML output + asserts.true( + env, + score_info.html_dir != None, + "Phase 2 should generate HTML directory", + ) + + return analysistest.end(env) + +two_phase_html_second_test = analysistest.make(_two_phase_html_second_test_impl) + +# ============================================================================ +# Config Generation Tests +# ============================================================================ + +def _config_auto_generation_test_impl(ctx): + """Test that conf.py is auto-generated when not provided.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + score_info = target_under_test[SphinxModuleInfo] + + # Module without explicit config should still build + asserts.true( + env, + score_info.html_dir != None, + "Auto-generated config should allow HTML generation", + ) + + return analysistest.end(env) + +config_auto_generation_test = analysistest.make(_config_auto_generation_test_impl) + +def _config_explicit_usage_test_impl(ctx): + """Test that explicit conf.py is used when provided.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + score_info = target_under_test[SphinxModuleInfo] + + # Module with explicit config should build + asserts.true( + env, + score_info.html_dir != None, + "Explicit config should allow HTML generation", + ) + + return analysistest.end(env) + +config_explicit_usage_test = analysistest.make(_config_explicit_usage_test_impl) + +# ============================================================================ +# Dependency Handling Tests +# ============================================================================ + +def _deps_html_merging_test_impl(ctx): + """Test that HTML from dependencies is merged into output.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + score_info = target_under_test[SphinxModuleInfo] + + # Module with dependencies should generate merged HTML + asserts.true( + env, + score_info.html_dir != None, + "Module with dependencies should generate merged HTML", + ) + + return analysistest.end(env) + +deps_html_merging_test = analysistest.make(_deps_html_merging_test_impl) + +def _deps_needs_collection_test_impl(ctx): + """Test that needs from dependencies are collected.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + needs_info = target_under_test[SphinxNeedsInfo] + transitive_needs = needs_info.needs_json_files.to_list() + + # Should collect needs from dependencies + asserts.true( + env, + len(transitive_needs) >= 1, + "Should collect needs.json from dependencies", + ) + + return analysistest.end(env) + +deps_needs_collection_test = analysistest.make(_deps_needs_collection_test_impl) + +# ============================================================================ +# Test Suite +# ============================================================================ + +def sphinx_module_providers_test_suite(name): + """Create a test suite for sphinx_module providers and build phases. + + Tests cover: + - Transitive needs.json collection + - Dependency handling (HTML merging, needs collection) + + Args: + name: Name of the test suite + """ + + native.test_suite( + name = name, + tests = [ + # Provider tests + ":score_needs_with_deps_test", + + # Dependency tests + ":deps_html_merging_test", + ":deps_needs_collection_test", + ], + ) diff --git a/bazel/rules/rules_score/test/test/seooc_test.bzl b/bazel/rules/rules_score/test/test/seooc_test.bzl new file mode 100644 index 00000000..64fd1ace --- /dev/null +++ b/bazel/rules/rules_score/test/test/seooc_test.bzl @@ -0,0 +1,135 @@ +""" +Test suite for dependable_element macro. + +Tests the SEooC (Safety Element out of Context) functionality including: +- Index generation with artifact references +- Integration with sphinx_module +- Sphinx-needs cross-referencing +- HTML output generation +""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("//bazel/rules/rules_score:providers.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") + +def _seooc_index_generation_test_impl(ctx): + """Test that dependable_element generates proper index.rst file.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the generated index file + files = target_under_test[DefaultInfo].files.to_list() + + # Find index.rst in the output files + index_file = None + for f in files: + if f.basename == "index.rst": + index_file = f + break + + # Assert index file exists + asserts.true( + env, + index_file != None, + "Expected index.rst to be generated by dependable_element_index rule", + ) + + return analysistest.end(env) + +seooc_index_generation_test = analysistest.make( + impl = _seooc_index_generation_test_impl, +) + +def _seooc_artifacts_copied_test_impl(ctx): + """Test that all dependable element artifacts are copied to output directory.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + files = target_under_test[DefaultInfo].files.to_list() + + # Expected artifact basenames - these come from the SphinxSourcesInfo providers + # and are filtered to only include .rst/.md files for the index + expected_artifacts = [ + "feat_req_feat_req_trlc.rst", # from requirements (feature_requirements rendered from TRLC) + "dfa.rst", # from :dependability_analysis_target + ] + + # Check each artifact exists + actual_basenames = [f.basename for f in files] + for artifact in expected_artifacts: + asserts.true( + env, + artifact in actual_basenames, + "Expected artifact '{}' to be in output files".format(artifact), + ) + + return analysistest.end(env) + +seooc_artifacts_copied_test = analysistest.make( + impl = _seooc_artifacts_copied_test_impl, +) + +def _seooc_sphinx_module_generated_test_impl(ctx): + """Test that dependable_element generates sphinx_module with HTML output.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Check that SphinxModuleInfo provider exists + asserts.true( + env, + SphinxModuleInfo in target_under_test, + "Expected dependable_element to provide SphinxModuleInfo from sphinx_module", + ) + + return analysistest.end(env) + +seooc_sphinx_module_generated_test = analysistest.make( + impl = _seooc_sphinx_module_generated_test_impl, +) + +def _seooc_needs_provider_test_impl(ctx): + """Test that dependable_element generates needs provider for cross-referencing.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Check that SphinxNeedsInfo provider exists + asserts.true( + env, + SphinxNeedsInfo in target_under_test, + "Expected dependable_element_needs to provide SphinxNeedsInfo", + ) + + return analysistest.end(env) + +seooc_needs_provider_test = analysistest.make( + impl = _seooc_needs_provider_test_impl, +) + +def _seooc_description_test_impl(ctx): + """Test that SEooC includes description in generated index.rst.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the generated index file + files = target_under_test[DefaultInfo].files.to_list() + + # Find index.rst + index_file = None + for f in files: + if f.basename == "index.rst": + index_file = f + break + + # Note: We can't easily read file contents in analysis test, + # but we can verify the file exists. The description content + # would be validated through integration tests or manual inspection. + asserts.true( + env, + index_file != None, + "Expected index.rst to exist for description validation", + ) + + return analysistest.end(env) + +seooc_description_test = analysistest.make( + impl = _seooc_description_test_impl, +) diff --git a/bazel/rules/rules_score/test/test/test_safety_analysis_tools.py b/bazel/rules/rules_score/test/test/test_safety_analysis_tools.py new file mode 100644 index 00000000..302e7835 --- /dev/null +++ b/bazel/rules/rules_score/test/test/test_safety_analysis_tools.py @@ -0,0 +1,359 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +"""Tests for safety_analysis_tools.""" + +import json +import logging +import os +import sys +import tempfile +import unittest + +from safety_analysis_tools import ( + LOBSTER_GENERATOR, + LOBSTER_SCHEMA, + LOBSTER_VERSION, + _is_valid_trlc_fqn, + _parse_quoted_args, + create_lobster_output, + extract_fta_items, + main, + preprocess_puml, +) + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + +SAMPLE_FTA = """\ +@startuml + +!include fta_metamodel.puml + +$TopEvent("Failure takes over", "Pkg.TopFailure") +$OrGate("OG1", "Pkg.TopFailure") +$BasicEvent("Bad luck", "Pkg.BadLuck", "OG1") +$IntermediateEvent("Angry", "IEF", "OG1") +$AndGate("AG2", "IEF") +$BasicEvent("No Cookies", "Pkg.NoCookies", "AG2") +$BasicEvent("No Coffee", "Pkg.NoCoffee", "AG2") + +@enduml +""" + +EMPTY_FTA = """\ +@startuml +' No events here +@enduml +""" + +SAMPLE_METAMODEL = """\ +@startuml + +' AND gate sprite +sprite $and placeholder + +!procedure $TopEvent($name, $alias) + rectangle "$name" as $alias +!endprocedure + +!procedure $BasicEvent($name, $alias, $connection) + "$name" as $alias + $alias -u-> $connection +!endprocedure + +@enduml +""" + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _write_file(content: str, directory: str, name: str) -> str: + path = os.path.join(directory, name) + with open(path, "w", encoding="utf-8") as fh: + fh.write(content) + return path + + +# --------------------------------------------------------------------------- +# _parse_quoted_args +# --------------------------------------------------------------------------- + + +class TestParseQuotedArgs(unittest.TestCase): + def test_returns_none_when_proc_absent(self): + self.assertIsNone(_parse_quoted_args("$OrGate()", "$TopEvent")) + + def test_extracts_two_args(self): + self.assertEqual( + _parse_quoted_args('$TopEvent("My name", "Pkg.Record")', "$TopEvent"), + ["My name", "Pkg.Record"], + ) + + def test_extracts_three_args(self): + self.assertEqual( + _parse_quoted_args( + '$BasicEvent("Bad luck", "Pkg.BadLuck", "OG1")', "$BasicEvent" + ), + ["Bad luck", "Pkg.BadLuck", "OG1"], + ) + + def test_returns_none_on_missing_open_paren(self): + self.assertIsNone(_parse_quoted_args("$TopEvent", "$TopEvent")) + + def test_returns_none_on_no_quoted_args(self): + self.assertIsNone(_parse_quoted_args("$TopEvent()", "$TopEvent")) + + def test_ignores_content_after_close_paren(self): + line = '$TopEvent("a", "b") $BasicEvent("c", "d", "e")' + self.assertEqual(_parse_quoted_args(line, "$TopEvent"), ["a", "b"]) + + +# --------------------------------------------------------------------------- +# _is_valid_trlc_fqn +# --------------------------------------------------------------------------- + + +class TestIsValidTrlcFqn(unittest.TestCase): + def test_valid(self): + self.assertTrue(_is_valid_trlc_fqn("Pkg.Record")) + self.assertTrue(_is_valid_trlc_fqn("SampleLib.FailureMode")) + + def test_no_dot(self): + self.assertFalse(_is_valid_trlc_fqn("NoDotsHere")) + + def test_too_many_dots(self): + self.assertFalse(_is_valid_trlc_fqn("A.B.C")) + + def test_empty_string(self): + self.assertFalse(_is_valid_trlc_fqn("")) + + def test_dot_only(self): + self.assertFalse(_is_valid_trlc_fqn(".")) + + +# --------------------------------------------------------------------------- +# extract_fta_items +# --------------------------------------------------------------------------- + + +class TestExtractFtaItems(unittest.TestCase): + def test_extracts_top_event(self): + with tempfile.TemporaryDirectory() as tmp: + path = _write_file(SAMPLE_FTA, tmp, "fta.puml") + items = extract_fta_items(path) + + top_events = [i for i in items if i["kind"] == "TopEvent"] + self.assertEqual(len(top_events), 1) + self.assertEqual(top_events[0]["name"], "Pkg.TopFailure") + self.assertEqual(top_events[0]["tag"], "fta Pkg.TopFailure") + self.assertIn("req Pkg.TopFailure", top_events[0]["refs"]) + + def test_extracts_basic_events(self): + with tempfile.TemporaryDirectory() as tmp: + path = _write_file(SAMPLE_FTA, tmp, "fta.puml") + items = extract_fta_items(path) + + basic_aliases = {i["name"] for i in items if i["kind"] == "BasicEvent"} + self.assertEqual( + basic_aliases, {"Pkg.BadLuck", "Pkg.NoCookies", "Pkg.NoCoffee"} + ) + + def test_ignores_gates_and_intermediate_events(self): + with tempfile.TemporaryDirectory() as tmp: + path = _write_file(SAMPLE_FTA, tmp, "fta.puml") + items = extract_fta_items(path) + self.assertEqual({i["kind"] for i in items}, {"TopEvent", "BasicEvent"}) + + def test_refs_point_to_alias(self): + with tempfile.TemporaryDirectory() as tmp: + path = _write_file(SAMPLE_FTA, tmp, "fta.puml") + for item in extract_fta_items(path): + self.assertIn(f"req {item['name']}", item["refs"]) + + def test_location_contains_file_and_line(self): + with tempfile.TemporaryDirectory() as tmp: + path = _write_file(SAMPLE_FTA, tmp, "fta.puml") + for item in extract_fta_items(path): + self.assertEqual(item["location"]["kind"], "file") + self.assertEqual(item["location"]["file"], path) + self.assertGreater(item["location"]["line"], 0) + + def test_one_match_per_line(self): + content = '$TopEvent("name", "A.B") $BasicEvent("x", "C.D", "conn")\n' + with tempfile.TemporaryDirectory() as tmp: + path = _write_file(content, tmp, "fta.puml") + items = extract_fta_items(path) + self.assertEqual(len(items), 1) + self.assertEqual(items[0]["kind"], "TopEvent") + + def test_empty_diagram_returns_empty_list(self): + with tempfile.TemporaryDirectory() as tmp: + path = _write_file(EMPTY_FTA, tmp, "fta.puml") + self.assertEqual(extract_fta_items(path), []) + + def test_missing_file_raises(self): + with tempfile.TemporaryDirectory() as tmp: + with self.assertRaises(OSError): + extract_fta_items(os.path.join(tmp, "nonexistent.puml")) + + def test_invalid_alias_logs_warning(self): + content = '$TopEvent("bad alias", "NoDotsHere")\n' + with tempfile.TemporaryDirectory() as tmp: + path = _write_file(content, tmp, "fta.puml") + with self.assertLogs(level="WARNING") as log: + extract_fta_items(path) + self.assertTrue(any("does not look like a valid" in m for m in log.output)) + + def test_valid_alias_produces_no_fqn_warning(self): + with tempfile.TemporaryDirectory() as tmp: + path = _write_file(SAMPLE_FTA, tmp, "fta.puml") + with self.assertLogs(level="WARNING") as log: + logging.getLogger().warning( + "sentinel" + ) # ensure assertLogs doesn't fail + extract_fta_items(path) + self.assertFalse(any("does not look like a valid" in m for m in log.output)) + + +# --------------------------------------------------------------------------- +# preprocess_puml +# --------------------------------------------------------------------------- + + +class TestPreprocessPuml(unittest.TestCase): + def _write_metamodel(self, directory: str) -> str: + return _write_file(SAMPLE_METAMODEL, directory, "fta_metamodel.puml") + + def test_inlines_metamodel_content(self): + with tempfile.TemporaryDirectory() as tmp: + meta = self._write_metamodel(tmp) + src = _write_file( + '@startuml\n!include fta_metamodel.puml\n$TopEvent("x", "A.B")\n@enduml\n', + tmp, + "fta.puml", + ) + out = os.path.join(tmp, "out.puml") + preprocess_puml(src, meta, out) + + content = open(out).read() + self.assertNotIn("!include fta_metamodel.puml", content) + self.assertIn("!procedure $TopEvent", content) + self.assertIn("sprite $and", content) + self.assertIn('$TopEvent("x", "A.B")', content) + + def test_metamodel_markers_are_stripped(self): + with tempfile.TemporaryDirectory() as tmp: + meta = self._write_metamodel(tmp) + src = _write_file( + "@startuml\n!include fta_metamodel.puml\n@enduml\n", + tmp, + "fta.puml", + ) + out = os.path.join(tmp, "out.puml") + preprocess_puml(src, meta, out) + self.assertNotIn("@startuml\n@startuml", open(out).read()) + + def test_diagram_without_include_is_unchanged(self): + original = '@startuml\n$TopEvent("x", "A.B")\n@enduml\n' + with tempfile.TemporaryDirectory() as tmp: + meta = self._write_metamodel(tmp) + src = _write_file(original, tmp, "fta.puml") + out = os.path.join(tmp, "out.puml") + preprocess_puml(src, meta, out) + self.assertEqual(open(out).read(), original) + + +# --------------------------------------------------------------------------- +# create_lobster_output +# --------------------------------------------------------------------------- + + +class TestCreateLobsterOutput(unittest.TestCase): + def test_envelope_fields(self): + items = [{"tag": "fta A.B", "name": "A.B"}] + output = create_lobster_output(items) + self.assertEqual(output["generator"], LOBSTER_GENERATOR) + self.assertEqual(output["schema"], LOBSTER_SCHEMA) + self.assertEqual(output["version"], LOBSTER_VERSION) + self.assertEqual(output["data"], items) + + def test_empty_items(self): + self.assertEqual(create_lobster_output([])["data"], []) + + +# --------------------------------------------------------------------------- +# main (CLI integration) +# --------------------------------------------------------------------------- + + +class TestMain(unittest.TestCase): + """Integration tests for the flat CLI: --metamodel --output-dir --lobster inputs...""" + + def _run_main(self, tmp: str, *puml_paths: str) -> dict: + """Invoke main() and return the parsed lobster JSON dict.""" + meta = _write_file(SAMPLE_METAMODEL, tmp, "fta_metamodel.puml") + out_dir = os.path.join(tmp, "preprocessed") + lobster_path = os.path.join(tmp, "out.lobster") + + saved = sys.argv + try: + sys.argv = [ + "safety_analysis_tools", + "--metamodel", + meta, + "--output-dir", + out_dir, + "--lobster", + lobster_path, + *puml_paths, + ] + main() + finally: + sys.argv = saved + + with open(lobster_path, encoding="utf-8") as fh: + return json.load(fh) + + def test_produces_valid_lobster_file(self): + with tempfile.TemporaryDirectory() as tmp: + puml = _write_file(SAMPLE_FTA, tmp, "fta.puml") + data = self._run_main(tmp, puml) + self.assertEqual(data["schema"], LOBSTER_SCHEMA) + self.assertEqual(len(data["data"]), 4) # 1 TopEvent + 3 BasicEvents + + def test_produces_preprocessed_puml_files(self): + with tempfile.TemporaryDirectory() as tmp: + puml = _write_file(SAMPLE_FTA, tmp, "fta.puml") + self._run_main(tmp, puml) + + preprocessed = os.path.join(tmp, "preprocessed", "fta.puml") + self.assertTrue(os.path.exists(preprocessed)) + content = open(preprocessed).read() + self.assertNotIn("!include fta_metamodel.puml", content) + self.assertIn("!procedure $TopEvent", content) + + def test_multiple_inputs_aggregate_lobster_items(self): + with tempfile.TemporaryDirectory() as tmp: + p1 = _write_file(SAMPLE_FTA, tmp, "a.puml") + p2 = _write_file(SAMPLE_FTA, tmp, "b.puml") + data = self._run_main(tmp, p1, p2) + self.assertEqual(len(data["data"]), 8) # 4 items × 2 files + + +if __name__ == "__main__": + unittest.main() diff --git a/bazel/rules/rules_score/test/test/unit_component_test.bzl b/bazel/rules/rules_score/test/test/unit_component_test.bzl new file mode 100644 index 00000000..dc607af9 --- /dev/null +++ b/bazel/rules/rules_score/test/test/unit_component_test.bzl @@ -0,0 +1,181 @@ +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* +""" +Test suite for unit, component, and dependable_element rules. + +Tests the new hierarchical structure for S-CORE process compliance: +- Unit: smallest testable element +- Component: collection of units +- Dependable Element: complete SEooC with full documentation +""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("//bazel/rules/rules_score:providers.bzl", "ComponentInfo", "SphinxSourcesInfo", "UnitInfo") + +# ============================================================================ +# Unit Tests +# ============================================================================ + +def _unit_provider_test_impl(ctx): + """Test that unit rule provides UnitInfo.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Check UnitInfo provider exists + asserts.true( + env, + UnitInfo in target_under_test, + "Unit should provide UnitInfo", + ) + + unit_info = target_under_test[UnitInfo] + + # Verify fields are populated + asserts.true( + env, + unit_info.name != None, + "UnitInfo should have name field", + ) + + asserts.true( + env, + unit_info.unit_design != None, + "UnitInfo should have unit_design field", + ) + + asserts.true( + env, + unit_info.implementation != None, + "UnitInfo should have implementation field", + ) + + asserts.true( + env, + unit_info.tests != None, + "UnitInfo should have tests field", + ) + + return analysistest.end(env) + +unit_provider_test = analysistest.make(_unit_provider_test_impl) + +def _unit_sphinx_sources_test_impl(ctx): + """Test that unit rule provides SphinxSourcesInfo.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Check SphinxSourcesInfo provider exists + asserts.true( + env, + SphinxSourcesInfo in target_under_test, + "Unit should provide SphinxSourcesInfo", + ) + + return analysistest.end(env) + +unit_sphinx_sources_test = analysistest.make(_unit_sphinx_sources_test_impl) + +# ============================================================================ +# Component Tests +# ============================================================================ + +def _component_provider_test_impl(ctx): + """Test that component rule provides ComponentInfo.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Check ComponentInfo provider exists + asserts.true( + env, + ComponentInfo in target_under_test, + "Component should provide ComponentInfo", + ) + + comp_info = target_under_test[ComponentInfo] + + # Verify fields are populated + asserts.true( + env, + comp_info.name != None, + "ComponentInfo should have name field", + ) + + asserts.true( + env, + comp_info.requirements != None, + "ComponentInfo should have component_requirements field", + ) + + asserts.true( + env, + comp_info.components != None, + "ComponentInfo should have components field", + ) + + asserts.true( + env, + comp_info.tests != None, + "ComponentInfo should have tests field", + ) + + asserts.true( + env, + comp_info.architecture != None, + "ComponentInfo should have architecture field", + ) + + return analysistest.end(env) + +component_provider_test = analysistest.make(_component_provider_test_impl) + +def _component_sphinx_sources_test_impl(ctx): + """Test that component rule provides SphinxSourcesInfo.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Check SphinxSourcesInfo provider exists + asserts.true( + env, + SphinxSourcesInfo in target_under_test, + "Component should provide SphinxSourcesInfo", + ) + + return analysistest.end(env) + +component_sphinx_sources_test = analysistest.make(_component_sphinx_sources_test_impl) + +# ============================================================================ +# Dependable Element Tests +# ============================================================================ +# Note: Provider tests removed as dependable_element no longer creates a +# separate provider target. The main target is now a sphinx_module. + +# ============================================================================ +# Test Suite Definition +# ============================================================================ + +def unit_component_test_suite(name): + """Create test suite for unit, component, and dependable_element rules. + + Args: + name: Name of the test suite + """ + native.test_suite( + name = name, + tests = [ + ":unit_provider_test", + ":unit_sphinx_sources_test", + ":component_provider_test", + ":component_sphinx_sources_test", + ], + ) From 2398193248ffcad6e96fd37770aae5ceb9f42cc8 Mon Sep 17 00:00:00 2001 From: Ulrich Huber Date: Tue, 14 Apr 2026 10:12:14 +0200 Subject: [PATCH 03/10] Add examples to rules_score --- bazel/rules/rules_score/examples/seooc/BUILD | 65 +++++++++ .../rules_score/examples/seooc/design/BUILD | 28 ++++ .../examples/seooc/design/arch_design.rst | 34 +++++ .../examples/seooc/design/public_api.puml | 21 +++ .../examples/seooc/design/static_design.puml | 27 ++++ .../rules_score/examples/seooc/docs/BUILD | 32 +++++ .../rules_score/examples/seooc/docs/aous.trlc | 22 +++ .../examples/seooc/docs/requirements/BUILD | 109 +++++++++++++++ .../assumed_system_requirements.trlc | 34 +++++ .../requirements/component_requirements.trlc | 51 +++++++ .../component_requirements_extra.trlc | 30 ++++ .../requirements/feature_requirements.trlc | 48 +++++++ .../examples/seooc/safety_analysis/BUILD | 71 ++++++++++ .../assets/safety_analysis.puml | 44 ++++++ .../assets/safety_analysis.svg | 55 ++++++++ .../assets/tool_data_flow.puml | 38 ++++++ .../safety_analysis/assets/tool_data_flow.svg | 1 + .../seooc/safety_analysis/safety_analysis.md | 128 ++++++++++++++++++ .../sample_fmea_control_measures.trlc | 36 +++++ .../sample_fmea_failure_modes.trlc | 33 +++++ .../seooc/safety_analysis/sample_fta.puml | 31 +++++ .../rules_score/examples/seooc/unit_1/BUILD | 51 +++++++ .../examples/seooc/unit_1/docs/BUILD | 28 ++++ .../seooc/unit_1/docs/public_interface.h | 20 +++ .../unit_1/docs/unit_1_class_diagram.puml | 11 ++ .../seooc/unit_1/docs/unit_1_design.rst | 21 +++ .../rules_score/examples/seooc/unit_1/foo.cpp | 20 +++ .../rules_score/examples/seooc/unit_1/foo.h | 32 +++++ .../examples/seooc/unit_1/foo_test.cpp | 25 ++++ .../rules_score/examples/seooc/unit_2/BUILD | 53 ++++++++ .../rules_score/examples/seooc/unit_2/bar.cpp | 32 +++++ .../rules_score/examples/seooc/unit_2/bar.h | 37 +++++ .../examples/seooc/unit_2/bar_test.cpp | 25 ++++ .../examples/seooc/unit_2/docs/BUILD | 29 ++++ .../unit_2/docs/unit_2_class_diagram.puml | 18 +++ .../seooc/unit_2/docs/unit_2_design.rst | 21 +++ .../examples/some_other_library/BUILD | 66 +++++++++ .../some_other_library/static_design.puml | 22 +++ validation/archver/README.md | 6 +- validation/archver/src/validation.rs | 8 +- 40 files changed, 1456 insertions(+), 7 deletions(-) create mode 100644 bazel/rules/rules_score/examples/seooc/BUILD create mode 100644 bazel/rules/rules_score/examples/seooc/design/BUILD create mode 100644 bazel/rules/rules_score/examples/seooc/design/arch_design.rst create mode 100644 bazel/rules/rules_score/examples/seooc/design/public_api.puml create mode 100644 bazel/rules/rules_score/examples/seooc/design/static_design.puml create mode 100644 bazel/rules/rules_score/examples/seooc/docs/BUILD create mode 100644 bazel/rules/rules_score/examples/seooc/docs/aous.trlc create mode 100644 bazel/rules/rules_score/examples/seooc/docs/requirements/BUILD create mode 100644 bazel/rules/rules_score/examples/seooc/docs/requirements/assumed_system_requirements.trlc create mode 100644 bazel/rules/rules_score/examples/seooc/docs/requirements/component_requirements.trlc create mode 100644 bazel/rules/rules_score/examples/seooc/docs/requirements/component_requirements_extra.trlc create mode 100644 bazel/rules/rules_score/examples/seooc/docs/requirements/feature_requirements.trlc create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.puml create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.svg create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/assets/tool_data_flow.puml create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/assets/tool_data_flow.svg create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/safety_analysis.md create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_control_measures.trlc create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_failure_modes.trlc create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.puml create mode 100644 bazel/rules/rules_score/examples/seooc/unit_1/BUILD create mode 100644 bazel/rules/rules_score/examples/seooc/unit_1/docs/BUILD create mode 100644 bazel/rules/rules_score/examples/seooc/unit_1/docs/public_interface.h create mode 100644 bazel/rules/rules_score/examples/seooc/unit_1/docs/unit_1_class_diagram.puml create mode 100644 bazel/rules/rules_score/examples/seooc/unit_1/docs/unit_1_design.rst create mode 100644 bazel/rules/rules_score/examples/seooc/unit_1/foo.cpp create mode 100644 bazel/rules/rules_score/examples/seooc/unit_1/foo.h create mode 100644 bazel/rules/rules_score/examples/seooc/unit_1/foo_test.cpp create mode 100644 bazel/rules/rules_score/examples/seooc/unit_2/BUILD create mode 100644 bazel/rules/rules_score/examples/seooc/unit_2/bar.cpp create mode 100644 bazel/rules/rules_score/examples/seooc/unit_2/bar.h create mode 100644 bazel/rules/rules_score/examples/seooc/unit_2/bar_test.cpp create mode 100644 bazel/rules/rules_score/examples/seooc/unit_2/docs/BUILD create mode 100644 bazel/rules/rules_score/examples/seooc/unit_2/docs/unit_2_class_diagram.puml create mode 100644 bazel/rules/rules_score/examples/seooc/unit_2/docs/unit_2_design.rst create mode 100644 bazel/rules/rules_score/examples/some_other_library/BUILD create mode 100644 bazel/rules/rules_score/examples/some_other_library/static_design.puml diff --git a/bazel/rules/rules_score/examples/seooc/BUILD b/bazel/rules/rules_score/examples/seooc/BUILD new file mode 100644 index 00000000..50a35a97 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/BUILD @@ -0,0 +1,65 @@ +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* + +load( + "//bazel/rules/rules_score:rules_score.bzl", + "component", + "dependability_analysis", + "dependable_element", +) + +cc_library( + name = "sample_library", + visibility = ["//visibility:public"], + deps = [ + "//bazel/rules/rules_score/examples/seooc/unit_1:unit_1_lib", + "//bazel/rules/rules_score/examples/seooc/unit_2:unit_2_lib", + ], +) + +component( + name = "component_example", + components = [ + "//bazel/rules/rules_score/examples/seooc/unit_1:unit_1", + "//bazel/rules/rules_score/examples/seooc/unit_2:unit_2", + ], + requirements = [ + "//bazel/rules/rules_score/examples/seooc/docs/requirements:component_requirements", + "//bazel/rules/rules_score/examples/seooc/docs/requirements:component_requirements_extra", + ], + tags = ["manual"], + tests = [], +) + +dependability_analysis( + name = "sample_dependability_analysis", + arch_design = "//bazel/rules/rules_score/examples/seooc/design:sample_seooc_design", + fmea = ["//bazel/rules/rules_score/examples/seooc/safety_analysis:sample_fmea"], +) + +dependable_element( + name = "safety_software_seooc_example", + architectural_design = ["//bazel/rules/rules_score/examples/seooc/design:sample_seooc_design"], + assumptions_of_use = [], + components = [":component_example"], + dependability_analysis = [ + ":sample_dependability_analysis", + ], + description = "Safety Element out of Context (SEooC) example", + integrity_level = "B", + requirements = [ + "//bazel/rules/rules_score/examples/seooc/docs/requirements:feature_requirements", + ], + tests = [], + deps = ["//bazel/rules/rules_score/examples/some_other_library:other_seooc"], +) diff --git a/bazel/rules/rules_score/examples/seooc/design/BUILD b/bazel/rules/rules_score/examples/seooc/design/BUILD new file mode 100644 index 00000000..86411807 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/design/BUILD @@ -0,0 +1,28 @@ +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* +load( + "//bazel/rules/rules_score:rules_score.bzl", + "architectural_design", +) + +architectural_design( + name = "sample_seooc_design", + public_api = [ + "public_api.puml", + ], + static = [ + "static_design.puml", + "arch_design.rst", + ], + visibility = ["//visibility:public"], +) diff --git a/bazel/rules/rules_score/examples/seooc/design/arch_design.rst b/bazel/rules/rules_score/examples/seooc/design/arch_design.rst new file mode 100644 index 00000000..fe43befd --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/design/arch_design.rst @@ -0,0 +1,34 @@ +.. + # ******************************************************************************* + # 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 + # ******************************************************************************* + +Architectural Design +==================== + +This is the architectural design of the Safety Software SEooC Example: + +Static Architecture +------------------- + +.. uml:: static_design.puml + :align: center + :alt: Static Component Architectural Design + :width: 100% + +Public API +---------- + +.. uml:: public_api.puml + :align: center + :alt: Public API of the SEooC Example + :width: 100% diff --git a/bazel/rules/rules_score/examples/seooc/design/public_api.puml b/bazel/rules/rules_score/examples/seooc/design/public_api.puml new file mode 100644 index 00000000..65810f92 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/design/public_api.puml @@ -0,0 +1,21 @@ +' ******************************************************************************* +' Copyright (c) 2025 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 +' ******************************************************************************* + +@startuml + +package "SampleLibraryAPI" as SampleLibraryAPI { + interface "GetNumber" as GetNumber + interface "SetNumber" as SetNumber +} + +@enduml diff --git a/bazel/rules/rules_score/examples/seooc/design/static_design.puml b/bazel/rules/rules_score/examples/seooc/design/static_design.puml new file mode 100644 index 00000000..c5a72503 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/design/static_design.puml @@ -0,0 +1,27 @@ +' ******************************************************************************* +' Copyright (c) 2025 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 +' ******************************************************************************* + +@startuml static_design + +package "Safety Software SEooC Example" as safety_software_seooc_example <> { + component "ComponentExample" as component_example <> { + component "Unit 1" as unit_1 <> + component "Unit 2" as unit_2 <> + } +} + +package "SampleLibraryAPI" as SampleLibraryAPI + +component_example --> SampleLibraryAPI + +@enduml diff --git a/bazel/rules/rules_score/examples/seooc/docs/BUILD b/bazel/rules/rules_score/examples/seooc/docs/BUILD new file mode 100644 index 00000000..e2d391f4 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/docs/BUILD @@ -0,0 +1,32 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +load("@trlc//:trlc.bzl", "trlc_requirements", "trlc_requirements_test") + +trlc_requirements( + name = "aous", + srcs = [ + "aous.trlc", + ], + spec = [ + "@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model", + ], + visibility = ["//visibility:public"], +) + +trlc_requirements_test( + name = "aous_test", + reqs = [ + ":aous", + ], + visibility = ["//visibility:public"], +) diff --git a/bazel/rules/rules_score/examples/seooc/docs/aous.trlc b/bazel/rules/rules_score/examples/seooc/docs/aous.trlc new file mode 100644 index 00000000..28ab9a13 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/docs/aous.trlc @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SampleType + +import ScoreReq + +ScoreReq.AoU SampleAoU { + description = "It shall be made sure that this SampleAoU never ends up anywhere" + safety = ScoreReq.Asil.B + mitigates = "ShmemCreatedWrongName" + version = 1 +} diff --git a/bazel/rules/rules_score/examples/seooc/docs/requirements/BUILD b/bazel/rules/rules_score/examples/seooc/docs/requirements/BUILD new file mode 100644 index 00000000..31448633 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/docs/requirements/BUILD @@ -0,0 +1,109 @@ +# ******************************************************************************* +# Copyright (c) 2024 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 +# ******************************************************************************* + +load("@trlc//:trlc.bzl", "trlc_requirements", "trlc_requirements_test") +load("//bazel/rules/rules_score:rules_score.bzl", "component_requirements", "feature_requirements") +load("//validation/ai_checker:ai_checker.bzl", "trlc_requirements_ai_test") + +trlc_requirements( + name = "assumed_system_requirements", + srcs = [ + "assumed_system_requirements.trlc", + ], + spec = [ + "//bazel/rules/rules_score/trlc/config:score_requirements_model", + ], + visibility = ["//visibility:public"], +) + +trlc_requirements_test( + name = "assumed_system_requirements_test", + reqs = [ + ":assumed_system_requirements", + ], + visibility = ["//visibility:public"], +) + +trlc_requirements( + name = "feature_requirements_trlc", + srcs = [ + "feature_requirements.trlc", + ], + spec = [ + "//bazel/rules/rules_score/trlc/config:score_requirements_model", + ], + visibility = ["//visibility:public"], + deps = [ + ":assumed_system_requirements", + ], +) + +feature_requirements( + name = "feature_requirements", + srcs = [":feature_requirements_trlc"], + visibility = ["//visibility:public"], +) + +trlc_requirements_ai_test( + name = "feature_requirements_ai_test", + reqs = [":feature_requirements_trlc"], + tags = ["manual"], +) + +trlc_requirements( + name = "component_requirements_trlc", + srcs = [ + "component_requirements.trlc", + ], + spec = [ + "//bazel/rules/rules_score/trlc/config:score_requirements_model", + ], + visibility = ["//visibility:public"], + deps = [ + ":assumed_system_requirements", + ":feature_requirements_trlc", + ], +) + +component_requirements( + name = "component_requirements", + srcs = [":component_requirements_trlc"], + visibility = ["//visibility:public"], +) + +trlc_requirements( + name = "component_requirements_extra_trlc", + srcs = [ + "component_requirements_extra.trlc", + ], + spec = [ + "//bazel/rules/rules_score/trlc/config:score_requirements_model", + ], + visibility = ["//visibility:public"], + deps = [ + ":assumed_system_requirements", + ":feature_requirements_trlc", + ], +) + +component_requirements( + name = "component_requirements_extra", + srcs = [":component_requirements_extra_trlc"], + visibility = ["//visibility:public"], +) + +trlc_requirements_ai_test( + name = "component_requirements_ai_test", + reqs = [":component_requirements_trlc"], + tags = ["manual"], +) diff --git a/bazel/rules/rules_score/examples/seooc/docs/requirements/assumed_system_requirements.trlc b/bazel/rules/rules_score/examples/seooc/docs/requirements/assumed_system_requirements.trlc new file mode 100644 index 00000000..f6bf9ef0 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/docs/requirements/assumed_system_requirements.trlc @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SampleSEooC + +import ScoreReq + +/////////////////////////////// +// Assumed System Requirements +// System level requirements for SEooC +/////////////////////////////// + +ScoreReq.AssumedSystemReq ASR_SAMPLE_001 { + description = "The system shall provide safe and reliable numeric value management through encapsulated classes" + safety = ScoreReq.Asil.B + version = 1 + rationale = "System-level requirement for managing numeric values in a safety-critical context" +} + +ScoreReq.AssumedSystemReq ASR_SAMPLE_002 { + description = "The system shall ensure proper lifecycle management of dependencies through well-defined interfaces" + safety = ScoreReq.Asil.B + version = 1 + rationale = "System-level requirement for dependency injection and memory safety" +} diff --git a/bazel/rules/rules_score/examples/seooc/docs/requirements/component_requirements.trlc b/bazel/rules/rules_score/examples/seooc/docs/requirements/component_requirements.trlc new file mode 100644 index 00000000..c5de7957 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/docs/requirements/component_requirements.trlc @@ -0,0 +1,51 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SampleComponent + +import ScoreReq +import SampleSEooC + +ScoreReq.CompReq REQ_COMP_001 { + description = "The numeric value management interface shall provide a read operation that returns a uint8_t value" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.FEAT_001@1] + version = 1 +} + +ScoreReq.CompReq REQ_COMP_002 { + description = "The numeric value management interface shall not be extensible by external code" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.FEAT_002@1] + version = 1 +} + +ScoreReq.CompReq REQ_COMP_003 { + description = "The numeric value validator shall return a boolean result indicating whether the managed numeric value equals 42" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.FEAT_003@1] + version = 1 +} + +ScoreReq.CompReq REQ_COMP_004 { + description = "The numeric value validator shall accept a numeric value manager instance as its sole constructor argument" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.FEAT_003@1, SampleSEooC.FEAT_004@1] + version = 1 +} + +ScoreReq.CompReq REQ_COMP_005 { + description = "The numeric value validator shall take exclusive ownership of the received numeric value manager instance" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.FEAT_004@1] + version = 1 +} diff --git a/bazel/rules/rules_score/examples/seooc/docs/requirements/component_requirements_extra.trlc b/bazel/rules/rules_score/examples/seooc/docs/requirements/component_requirements_extra.trlc new file mode 100644 index 00000000..17a4ff54 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/docs/requirements/component_requirements_extra.trlc @@ -0,0 +1,30 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SampleComponentExtra + +import ScoreReq +import SampleSEooC + +ScoreReq.CompReq REQ_COMP_EXTRA_001 { + description = "The numeric value management interface shall initialize its internal value to zero upon construction" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.FEAT_001@1] + version = 1 +} + +ScoreReq.CompReq REQ_COMP_EXTRA_002 { + description = "The numeric value management interface shall guarantee that the stored value is never modified after construction" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.FEAT_002@1] + version = 1 +} diff --git a/bazel/rules/rules_score/examples/seooc/docs/requirements/feature_requirements.trlc b/bazel/rules/rules_score/examples/seooc/docs/requirements/feature_requirements.trlc new file mode 100644 index 00000000..1123a0cf --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/docs/requirements/feature_requirements.trlc @@ -0,0 +1,48 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SampleSEooC + +import ScoreReq + +/////////////////////////////// +// Feature Requirements +// Integration level requirements derived from Assumed System Requirements +/////////////////////////////// + +ScoreReq.FeatReq FEAT_001 { + description = "The component shall provide a numeric value management interface that returns a `uint8_t` value on every read access" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.ASR_SAMPLE_001@1] + version = 1 +} + +ScoreReq.FeatReq FEAT_002 { + description = "The component shall ensure that the numeric value management interface is not extensible by code outside the component boundary" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.ASR_SAMPLE_001@1] + version = 1 +} + +ScoreReq.FeatReq FEAT_003 { + description = "The component shall provide a validation interface that accepts a numeric value manager as its input and returns a boolean result" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.ASR_SAMPLE_002@1] + version = 1 +} + +ScoreReq.FeatReq FEAT_004 { + description = "The component shall ensure that the validation interface takes exclusive ownership of the provided numeric value manager for the duration of its lifetime" + safety = ScoreReq.Asil.B + derived_from = [SampleSEooC.ASR_SAMPLE_002@1] + version = 1 +} diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD b/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD new file mode 100644 index 00000000..8ccecab3 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD @@ -0,0 +1,71 @@ +# ******************************************************************************* +# Copyright (c) 2024 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 +# ******************************************************************************* + +load("@trlc//:trlc.bzl", "trlc_requirements", "trlc_requirements_test") +load( + "//bazel/rules/rules_score:rules_score.bzl", + "fmea", +) + +# FMEA +trlc_requirements( + name = "sample_fmea_failure_modes", + srcs = [ + "sample_fmea_failure_modes.trlc", + ], + spec = [ + "@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model", + ], + visibility = ["//visibility:public"], +) + +trlc_requirements( + name = "sample_fmea_control_measures", + srcs = [ + "sample_fmea_control_measures.trlc", + ], + spec = [ + "@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model", + ], + visibility = ["//visibility:public"], +) + +trlc_requirements_test( + name = "sample_fmea_test", + reqs = [ + ":sample_fmea_failure_modes", + ":sample_fmea_control_measures", + ], + visibility = ["//visibility:public"], +) + +# FTA + +filegroup( + name = "sample_fta", + srcs = [ + "sample_fta.puml", + ], + visibility = ["//visibility:public"], +) + +fmea( + name = "sample_fmea", + arch_design = "//bazel/rules/rules_score/examples/seooc/design:sample_seooc_design", + controlmeasures = [ + ":sample_fmea_control_measures", + ], + failuremodes = [":sample_fmea_failure_modes"], + root_causes = [":sample_fta"], + visibility = ["//visibility:public"], +) diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.puml b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.puml new file mode 100644 index 00000000..bff22765 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.puml @@ -0,0 +1,44 @@ +' ******************************************************************************* +' Copyright (c) {year} 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 +' ******************************************************************************* + +@startuml + +object "SEooC" as SEooC +object "Safety Analysis" as SafetyAnalysis +object "Failure Mode and Effects Analysis\n(FMEA)" as FMEA +object "Fault Tree Analysis\n(FTA)" as FTA +object "Failure Mode" as FailureMode +object "Root Cause" as RootCause +object "Safety Goal" as SafetyGoal +object "Measure" as Measure +object "Assumption of Use\n(AoU)" as AoU + +SafetyAnalysis <|-l- FMEA +SafetyAnalysis <|-- FTA + +SEooC "1" -u-> "n" SafetyGoal +SEooC <.r. FMEA : is conducted +FailureMode <.r. FTA : is conducted +FailureMode "1" o-- "n" RootCause + +SEooC "1" o-- "n" FailureMode +SEooC "1" o-- "n" AoU +FMEA ..> FailureMode : determine +FTA ..> RootCause : determine + +Measure <|-u- AoU +RootCause <-l- Measure : mitigates + + + +@enduml diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.svg b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.svg new file mode 100644 index 00000000..9742084c --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/safety_analysis.svg @@ -0,0 +1,55 @@ +SEooCSafety AnalysisFailure Mode and Effects Analysis(FMEA)Fault Tree Analysis(FTA)Failure ModeRoot CauseSafety GoalMeasureAssumption of Use(AoU)n1is conductedis conducted1n1n1ndeterminedeterminemitigates diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/tool_data_flow.puml b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/tool_data_flow.puml new file mode 100644 index 00000000..7b21c41c --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/tool_data_flow.puml @@ -0,0 +1,38 @@ +' ******************************************************************************* +' 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 +' ******************************************************************************* + +@startuml +left to right direction +rectangle "public_api.puml" as arch +rectangle "failuremodes.trlc" as fm +rectangle "controlmeasures.trlc" as cm +rectangle "fta.puml" as fta +rectangle "architecture.lobster" as arch_lob +rectangle "failuremodes.lobster" as fm_lob +rectangle "controlmeasures.lobster" as cm_lob +rectangle "root_causes.lobster" as rc_lob +rectangle "lobster_sa.conf" as conf +rectangle "report.json" as json +rectangle "report.html" as html + +arch --> arch_lob : puml_parser +fm --> fm_lob : lobster-trlc +cm --> cm_lob : lobster-trlc +fta --> rc_lob : safety_analysis_tools +arch_lob --> conf +fm_lob --> conf +cm_lob --> conf +rc_lob --> conf +conf --> json : lobster-ci-report +json --> html : lobster-html-report +@enduml diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/tool_data_flow.svg b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/tool_data_flow.svg new file mode 100644 index 00000000..de76b320 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/tool_data_flow.svg @@ -0,0 +1 @@ +public_api.pumlfailuremodes.trlccontrolmeasures.trlcfta.pumlarchitecture.lobsterfailuremodes.lobstercontrolmeasures.lobsterroot_causes.lobsterlobster_sa.confreport.jsonreport.htmlpuml_parserlobster-trlclobster-trlcsafety_analysis_toolslobster-ci-reportlobster-html-report diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/safety_analysis.md b/bazel/rules/rules_score/examples/seooc/safety_analysis/safety_analysis.md new file mode 100644 index 00000000..7a2f9416 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/safety_analysis.md @@ -0,0 +1,128 @@ +# Safety Analysis + +This document shows the workflow and the interrelation on how to document an FMEA in TRLC / PlantUml / Lobster. + +## Overview + +The current proposal on how the Safety Analysis according ISO 26262 Pt9 shall be implemented is as described in the Model: + +![Safety Analysis](assets/safety_analysis.svg) + +## Implementation + +For the implementation of the Safety Analysis a Mix of TRLC and Plantuml is applied. The Verification itself is performed in lobster at the end. + +For the Definition and Verification of the Safetyanalysis itself two Bazel rules exist: + +```starlark +load("//bazel/rules/rules_score:rules_score.bzl", "dependability_analysis", "fmea") + +fmea( + name = "sample_fmea", + controlmeasures = [], + failuremodes = [], + root_causes = [], +) + +dependability_analysis( + name = "sample_dependability_analysis", + fmea = [":sample_fmea"], +) +``` + + +### Failuremode + +It starts with a Failuremode which was determined in an FMEA. This Failuremode is defined in a TRLC format: + +```trlc +package SampleLibrary + +import ScoreReq + +ScoreReq.FailureMode SampleFailureMode{ + guideword = ScoreReq.GuideWord.LossOfFunction + description = "SampleFailureMode takes over the world" + failureeffect = "The world as we know it will end" + version = 1 + safety = ScoreReq.Asil.B + interface = "SampleLibraryAPI.GetNumber" +} +``` + +### Root Causes +As described in the metamodel the root causes (aka BasicEvents) shall be identified by performing an FTA on each Failuremode. + +The FTA shall be modeled using Plantuml. Therefore a Metamodel was defined using plantuml procedures. It includes following entities: + +Events: +- $TopEvent($name, $alias) +- $IntermediateEvent($name, $alias, $connection) +- $BasicEvent($name, $alias, $connection) + +Gates: +- $AndGate($alias, $connection) +- $OrGate($alias, $connection) + +The Matching between TRLC and Plantuml shall be performed using the TRLC ID. This means that also the TopEvent of the FTA shall use the TRLC ID of the Failuremode. For our case the Failuremode was defined in the package SampleLibrary. Therefore the Full ID of the TRLC node is: +SampleLibrary.SampleFailureMode + +```plantuml +@startuml + +!include fta_metamodel.puml + +' Top level (skeleton) +$TopEvent("SampleFailureMode takes over the world", "SampleLibrary.SampleFailureMode") + +' 2nd level gates and events +$OrGate("OG1", "SampleLibrary.SampleFailureMode") + +$IntermediateEvent("SampleFailureMode is Angry", "IEF", "OG1") +$BasicEvent("Just bad luck", "SampleLibrary.JustBadLuck", "OG1") + +' 3rd level cascades from AGF +$AndGate("AG2", "IEF") +$BasicEvent("No More Cookies", "SampleLibrary.NoMoreCookies", "AG2") +$BasicEvent("No More Coffee", "SampleLibrary.NoMoreCoffee", "AG2") +@enduml + +``` + +## Control Measures +For each BasicEvent a Control Measure shall be derived. This will be performed again in TRLC. The Mapping between PlantUML and TRLC will be done again based on the ID. So for our case we will define a Control Measure for NoMoreCookies: + +```trlc +ScoreReq.ControlMeasure SampleAoU{ + safety = ScoreReq.Asil.B + description = "We shall only order family size cookie jars" + version = 1 + mitigates = "SampleLibrary.NoMoreCookies" +} + +``` + +## Traceability Report + +The `dependability_analysis` rule wrapping the `fmea` target is a Bazel test rule that runs a [lobster](https://github.com/bmw-software-engineering/lobster) traceability report via `lobster-ci-report`. + +To run the report and check the traceability chain (FTA events → Failure Modes / Control Measures): + +```bash +bazel test //bazel/rules/rules_score/examples/seooc:sample_dependability_analysis +``` + +## Tool Data Flow + +The diagram below shows how the input files are processed by each tool and assembled into the final lobster traceability report. + +![Tool Data Flow](assets/tool_data_flow.svg) + +| Input | Tool | Output | +|---|---|---| +| `public_api.puml` | `puml_parser --fbs-output-dir --lobster-output-dir` | `architecture.lobster` — Architecture interface items | +| `failuremodes.trlc` | `lobster-trlc` | `failuremodes.lobster` — Failure Mode requirements | +| `controlmeasures.trlc` | `lobster-trlc` | `controlmeasures.lobster` — Control Measure requirements | +| `fta.puml` | `safety_analysis_tools` | `root_causes.lobster` — FTA TopEvent / BasicEvent activities | +| all `.lobster` files | `lobster-ci-report` | `report.json` — traceability check result | +| `report.json` | `lobster-html-report` | `report.html` — human-readable HTML report | diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_control_measures.trlc b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_control_measures.trlc new file mode 100644 index 00000000..59c9bc44 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_control_measures.trlc @@ -0,0 +1,36 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SampleLibrary + +import ScoreReq + +ScoreReq.ControlMeasure JustBadLuck{ + safety = ScoreReq.Asil.B + description = "Sometimes, the dark side wins. We shall be prepared for that." + version = 1 + mitigates = "SampleLibrary.JustBadLuck" +} + +ScoreReq.ControlMeasure NoMoreCookies{ + safety = ScoreReq.Asil.B + description = "We shall only order family size cookie jars" + version = 1 + mitigates = "SampleLibrary.NoMoreCookies" +} + +ScoreReq.ControlMeasure NoMoreCoffee{ + safety = ScoreReq.Asil.B + description = "We shall keep a coffee reserve for emergencies" + version = 1 + mitigates = "SampleLibrary.NoMoreCoffee" +} diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_failure_modes.trlc b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_failure_modes.trlc new file mode 100644 index 00000000..73ccd8fa --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_failure_modes.trlc @@ -0,0 +1,33 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ +package SampleLibrary + +import ScoreReq + +ScoreReq.FailureMode SampleFailureMode{ + guideword = ScoreReq.GuideWord.LossOfFunction + description = "SampleFailureMode takes over the world" + failureeffect = "The world as we know it will end" + version = 1 + safety = ScoreReq.Asil.B + interface = "SampleLibraryAPI.GetNumber" +} + +ScoreReq.FailureMode SampleFailureModeSetNumber{ + guideword = ScoreReq.GuideWord.LossOfFunction + description = "SampleFailureModeSetNumber takes over the world" + failureeffect = "The world as we know it will end" + version = 1 + safety = ScoreReq.Asil.B + interface = "SampleLibraryAPI.SetNumber" +} diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.puml b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.puml new file mode 100644 index 00000000..ea589d16 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.puml @@ -0,0 +1,31 @@ +' ******************************************************************************* +' Copyright (c) 2025 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 +' ******************************************************************************* + +@startuml + +!include fta_metamodel.puml + +' Top level (skeleton) +$TopEvent("SampleFailureMode takes over the world", "SampleLibrary.SampleFailureMode") + +' 2nd level gates and events +$OrGate("OG1", "SampleLibrary.SampleFailureMode") + +$IntermediateEvent("SampleFailureMode is Angry", "IEF", "OG1") +$BasicEvent("Just bad luck", "SampleLibrary.JustBadLuck", "OG1") + +' 3rd level cascades from AGF +$AndGate("AG2", "IEF") +$BasicEvent("No More Cookies", "SampleLibrary.NoMoreCookies", "AG2") +$BasicEvent("No More Coffee", "SampleLibrary.NoMoreCoffee", "AG2") +@enduml diff --git a/bazel/rules/rules_score/examples/seooc/unit_1/BUILD b/bazel/rules/rules_score/examples/seooc/unit_1/BUILD new file mode 100644 index 00000000..0c65e425 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_1/BUILD @@ -0,0 +1,51 @@ +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* +load( + "//bazel/rules/rules_score:rules_score.bzl", + "unit", +) + +unit( + name = "unit_1", + # scope = ["//bazel/rules/rules_score/examples/seooc/unit_1:__pkg__"], + # scope = ["//bazel/rules/rules_score/examples/seooc:__subpackages__"], + scope = ["//bazel/rules/rules_score/examples/seooc/unit_1:unit_1_lib"], + tests = [ + ":unit_1_test", + ], + unit_design = [ + "//bazel/rules/rules_score/examples/seooc/unit_1/docs:unit_design", + ], + visibility = ["//visibility:public"], + implementation = [ + ":unit_1_lib", + ], +) + +cc_library( + name = "unit_1_lib", + srcs = ["foo.cpp"], + hdrs = ["foo.h"], + visibility = ["//visibility:public"], + deps = ["//bazel/rules/rules_score/examples/some_other_library"], +) + +cc_test( + name = "unit_1_test", + srcs = ["foo_test.cpp"], + visibility = ["//visibility:public"], + deps = [ + ":unit_1_lib", + "@googletest//:gtest_main", + ], +) diff --git a/bazel/rules/rules_score/examples/seooc/unit_1/docs/BUILD b/bazel/rules/rules_score/examples/seooc/unit_1/docs/BUILD new file mode 100644 index 00000000..2abb961a --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_1/docs/BUILD @@ -0,0 +1,28 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +load( + "//bazel/rules/rules_score:rules_score.bzl", + "unit_design", +) + +# Class diagrams are passed through directly to Sphinx (sphinxcontrib-plantuml +# renders them at doc-build time). The RST wrapper references the .puml file +# via '.. uml::' so both must be listed together in 'static'. +unit_design( + name = "unit_design", + static = glob([ + "*.puml", + "*.rst", + ]), + visibility = ["//visibility:public"], +) diff --git a/bazel/rules/rules_score/examples/seooc/unit_1/docs/public_interface.h b/bazel/rules/rules_score/examples/seooc/unit_1/docs/public_interface.h new file mode 100644 index 00000000..fb6f3aea --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_1/docs/public_interface.h @@ -0,0 +1,20 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +// Public interface for unit_1 +#ifndef UNIT_1_PUBLIC_INTERFACE_H +#define UNIT_1_PUBLIC_INTERFACE_H + +// Example public API +void unit_1_public_function(); + +#endif // UNIT_1_PUBLIC_INTERFACE_H diff --git a/bazel/rules/rules_score/examples/seooc/unit_1/docs/unit_1_class_diagram.puml b/bazel/rules/rules_score/examples/seooc/unit_1/docs/unit_1_class_diagram.puml new file mode 100644 index 00000000..fafab979 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_1/docs/unit_1_class_diagram.puml @@ -0,0 +1,11 @@ +@startuml unit_1_class_diagram + +namespace unit_1 { + class Foo { + + + GetNumber() : uint8_t + + SetNumber(value : uint8_t) : void + } +} + +@enduml diff --git a/bazel/rules/rules_score/examples/seooc/unit_1/docs/unit_1_design.rst b/bazel/rules/rules_score/examples/seooc/unit_1/docs/unit_1_design.rst new file mode 100644 index 00000000..cb1d0aa6 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_1/docs/unit_1_design.rst @@ -0,0 +1,21 @@ +.. + # ******************************************************************************* + # 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 + # ******************************************************************************* + +Unit 1 Class Design +^^^^^^^^^^^^^^^^^^^ + +.. uml:: unit_1_class_diagram.puml + :align: center + :alt: Unit 1 Class Diagram + :width: 100% diff --git a/bazel/rules/rules_score/examples/seooc/unit_1/foo.cpp b/bazel/rules/rules_score/examples/seooc/unit_1/foo.cpp new file mode 100644 index 00000000..d8943bd8 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_1/foo.cpp @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ + +#include "bazel/rules/rules_score/examples/seooc/unit_1/foo.h" + +namespace unit_1 { + +// trace: SampleComponent.REQ_COMP_001 SampleLibraryAPI.GetNumber +std::uint8_t Foo::GetNumber() const { return 42u; } +} // namespace unit_1 diff --git a/bazel/rules/rules_score/examples/seooc/unit_1/foo.h b/bazel/rules/rules_score/examples/seooc/unit_1/foo.h new file mode 100644 index 00000000..30457e8c --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_1/foo.h @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ + +#ifndef FOO_H +#define FOO_H + +#include + +namespace unit_1 { + +// trace: SampleComponent.REQ_COMP_002 +class Foo final { +public: + // trace: SampleComponent.REQ_COMP_001 SampleLibraryAPI.GetNumber + std::uint8_t GetNumber() const; + // trace: SampleLibraryAPI.SetNumber + void SetNumber(std::uint8_t value); +}; + +} // namespace unit_1 + +#endif // FOO_H diff --git a/bazel/rules/rules_score/examples/seooc/unit_1/foo_test.cpp b/bazel/rules/rules_score/examples/seooc/unit_1/foo_test.cpp new file mode 100644 index 00000000..19d98244 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_1/foo_test.cpp @@ -0,0 +1,25 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ + +#include "bazel/rules/rules_score/examples/seooc/unit_1/foo.h" + +#include + +TEST(Foo, GetNumber) { + ::testing::Test::RecordProperty("lobster-tracing", + "SampleComponent.REQ_COMP_001"); + + unit_1::Foo unit{}; + + EXPECT_EQ(unit.GetNumber(), 42u); +} diff --git a/bazel/rules/rules_score/examples/seooc/unit_2/BUILD b/bazel/rules/rules_score/examples/seooc/unit_2/BUILD new file mode 100644 index 00000000..f356759c --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_2/BUILD @@ -0,0 +1,53 @@ +# ******************************************************************************* +# Copyright (c) 2025 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 +# ******************************************************************************* +load( + "//bazel/rules/rules_score:rules_score.bzl", + "unit", +) + +unit( + name = "unit_2", + # scope = ["//bazel/rules/rules_score/examples/seooc/unit_2:__pkg__"], + # scope = ["//bazel/rules/rules_score/examples/seooc:__subpackages__"], + scope = ["//bazel/rules/rules_score/examples/seooc/unit_2:__subpackages__"], + tests = [ + ":unit_2_test", + ], + unit_design = [ + "//bazel/rules/rules_score/examples/seooc/unit_2/docs:unit_design", + ], + visibility = ["//visibility:public"], + implementation = [ + ":unit_2_lib", + ], +) + +cc_library( + name = "unit_2_lib", + srcs = ["bar.cpp"], + hdrs = ["bar.h"], + visibility = ["//visibility:public"], + deps = [ + "//bazel/rules/rules_score/examples/seooc/unit_1:unit_1_lib", + ], +) + +cc_test( + name = "unit_2_test", + srcs = ["bar_test.cpp"], + visibility = ["//visibility:public"], + deps = [ + ":unit_2_lib", + "@googletest//:gtest_main", + ], +) diff --git a/bazel/rules/rules_score/examples/seooc/unit_2/bar.cpp b/bazel/rules/rules_score/examples/seooc/unit_2/bar.cpp new file mode 100644 index 00000000..d1dd155d --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_2/bar.cpp @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ + +#include "bazel/rules/rules_score/examples/seooc/unit_2/bar.h" + +#include + +namespace { +std::uint8_t kExpectedNumber = 42u; +} + +namespace unit_2 { + +// trace: SampleComponent.REQ_COMP_004 SampleComponent.REQ_COMP_005 +Bar::Bar(std::unique_ptr foo) : foo_{std::move(foo)} {} + +// trace: SampleComponent.REQ_COMP_003 +bool Bar::AssertNumber() const { + assert(foo_ != nullptr); + return foo_->GetNumber() == kExpectedNumber; +} +} // namespace unit_2 diff --git a/bazel/rules/rules_score/examples/seooc/unit_2/bar.h b/bazel/rules/rules_score/examples/seooc/unit_2/bar.h new file mode 100644 index 00000000..ce4ac2a5 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_2/bar.h @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ + +#ifndef BAR_H +#define BAR_H + +#include "bazel/rules/rules_score/examples/seooc/unit_1/foo.h" + +#include +#include + +namespace unit_2 { + +// trace: SampleComponent.REQ_COMP_003 SampleComponent.REQ_COMP_004 +// SampleComponent.REQ_COMP_005 +class Bar final { +public: + explicit Bar(std::unique_ptr); + bool AssertNumber() const; + +private: + std::unique_ptr foo_; +}; + +} // namespace unit_2 + +#endif // BAR_H diff --git a/bazel/rules/rules_score/examples/seooc/unit_2/bar_test.cpp b/bazel/rules/rules_score/examples/seooc/unit_2/bar_test.cpp new file mode 100644 index 00000000..17334981 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_2/bar_test.cpp @@ -0,0 +1,25 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ + +#include "bazel/rules/rules_score/examples/seooc/unit_2/bar.h" + +#include + +TEST(Bar, AssertNumber) { + ::testing::Test::RecordProperty( + "lobster-tracing", "SampleComponent.REQ_COMP_003,SampleComponent.REQ_" + "COMP_004,SampleComponent.REQ_COMP_005"); + unit_2::Bar unit{std::make_unique()}; + + EXPECT_TRUE(unit.AssertNumber()); +} diff --git a/bazel/rules/rules_score/examples/seooc/unit_2/docs/BUILD b/bazel/rules/rules_score/examples/seooc/unit_2/docs/BUILD new file mode 100644 index 00000000..4b7777fb --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_2/docs/BUILD @@ -0,0 +1,29 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +load( + "//bazel/rules/rules_score:rules_score.bzl", + "unit_design", +) + +# Class diagrams are passed through directly to Sphinx (sphinxcontrib-plantuml +# renders them at doc-build time). Both the RST wrapper and the referenced +# .puml file must be listed together so they are symlinked into the same output +# directory and the '.. uml::' directive can find the diagram. +unit_design( + name = "unit_design", + static = [ + "unit_2_design.rst", + "unit_2_class_diagram.puml", + ], + visibility = ["//visibility:public"], +) diff --git a/bazel/rules/rules_score/examples/seooc/unit_2/docs/unit_2_class_diagram.puml b/bazel/rules/rules_score/examples/seooc/unit_2/docs/unit_2_class_diagram.puml new file mode 100644 index 00000000..dd0d77b6 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_2/docs/unit_2_class_diagram.puml @@ -0,0 +1,18 @@ +@startuml unit_2_class_diagram + +namespace unit_1 { + class Foo { + + GetNumber() : uint8_t + + SetNumber(value : uint8_t) : void + } +} + +namespace unit_2 { + class Bar { + + AssertNumber() : bool + } +} + +Bar --> Foo : uses + +@enduml diff --git a/bazel/rules/rules_score/examples/seooc/unit_2/docs/unit_2_design.rst b/bazel/rules/rules_score/examples/seooc/unit_2/docs/unit_2_design.rst new file mode 100644 index 00000000..533e21c8 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/unit_2/docs/unit_2_design.rst @@ -0,0 +1,21 @@ +.. + # ******************************************************************************* + # 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 + # ******************************************************************************* + +Unit 2 Class Design +^^^^^^^^^^^^^^^^^^^ + +.. uml:: unit_2_class_diagram.puml + :align: center + :alt: Unit 2 Class Diagram + :width: 100% diff --git a/bazel/rules/rules_score/examples/some_other_library/BUILD b/bazel/rules/rules_score/examples/some_other_library/BUILD new file mode 100644 index 00000000..eb7d5750 --- /dev/null +++ b/bazel/rules/rules_score/examples/some_other_library/BUILD @@ -0,0 +1,66 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +load( + "//bazel/rules/rules_score:rules_score.bzl", + "architectural_design", + "component", + "dependable_element", + "unit", +) + +architectural_design( + name = "sample_seooc_design", + static = [ + "static_design.puml", + ], +) + +unit( + name = "abc", + scope = ["//bazel/rules/rules_score/examples/some_other_library:__pkg__"], + tests = [], + unit_design = [], + implementation = [ + ":some_other_library", + ], +) + +component( + name = "component_example", + components = [ + ":abc", + ], + requirements = ["//bazel/rules/rules_score/examples/seooc/docs/requirements:component_requirements"], + tags = ["manual"], + tests = [], +) + +dependable_element( + name = "other_seooc", + architectural_design = [":sample_seooc_design"], + assumptions_of_use = [], + components = [":component_example"], + dependability_analysis = [], + description = "XYZ", + integrity_level = "D", + requirements = [], + tests = [], + visibility = ["//:__subpackages__"], +) + +cc_library( + name = "some_other_library", + srcs = [], + visibility = ["//:__subpackages__"], + deps = [], +) diff --git a/bazel/rules/rules_score/examples/some_other_library/static_design.puml b/bazel/rules/rules_score/examples/some_other_library/static_design.puml new file mode 100644 index 00000000..b07ca55e --- /dev/null +++ b/bazel/rules/rules_score/examples/some_other_library/static_design.puml @@ -0,0 +1,22 @@ +' ******************************************************************************* +' Copyright (c) 2025 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 +' ******************************************************************************* + +@startuml + +package "Other SeooC" as other_seooc <> { + component "ComponentExample" as component_example <> { + component "ABC" as abc <> + } +} + +@enduml diff --git a/validation/archver/README.md b/validation/archver/README.md index 31639a9b..fb8b0eb0 100644 --- a/validation/archver/README.md +++ b/validation/archver/README.md @@ -67,9 +67,9 @@ is emitted. ### Example If Bazel build graph has: - Dependable element: `safety_software_seooc_example` → key: `safety_software_seooc_example` -- Component: `@//examples/seooc:component_example` → key: `component_example` (parent: `safety_software_seooc_example`) -- Unit: `@//examples/seooc/unit_1:unit_1` → key: `unit_1` (parent: `component_example`) -- Unit: `@//examples/seooc/unit_2:unit_2` → key: `unit_2` (parent: `component_example`) +- Component: `@//bazel/rules/rules_score/examples/seooc:component_example` → key: `component_example` (parent: `safety_software_seooc_example`) +- Unit: `@//bazel/rules/rules_score/examples/seooc/unit_1:unit_1` → key: `unit_1` (parent: `component_example`) +- Unit: `@//bazel/rules/rules_score/examples/seooc/unit_2:unit_2` → key: `unit_2` (parent: `component_example`) PlantUML must have: ```plantuml diff --git a/validation/archver/src/validation.rs b/validation/archver/src/validation.rs index 2076585c..4b90662a 100644 --- a/validation/archver/src/validation.rs +++ b/validation/archver/src/validation.rs @@ -286,13 +286,13 @@ mod tests { ( "safety_software_seooc_example", vec![], - vec!["@//examples/seooc:component_example"], + vec!["@//bazel/rules/rules_score/examples/seooc:component_example"], ), ( - "@//examples/seooc:component_example", + "@//bazel/rules/rules_score/examples/seooc:component_example", vec![ - "@//examples/seooc/unit_1:unit_1", - "@//examples/seooc/unit_2:unit_2", + "@//bazel/rules/rules_score/examples/seooc/unit_1:unit_1", + "@//bazel/rules/rules_score/examples/seooc/unit_2:unit_2", ], vec![], ), From dc4038e25f0c0d0746c838e295ecfd003b1924b8 Mon Sep 17 00:00:00 2001 From: Ulrich Huber Date: Tue, 14 Apr 2026 09:59:20 +0200 Subject: [PATCH 04/10] Remove old broken tests The tests will be recreated in the future, based on the updated rules implementation. --- bazel/rules/rules_score/BUILD | 1 - bazel/rules/rules_score/test/.bazelrc | 11 - bazel/rules/rules_score/test/.bazelversion | 1 - bazel/rules/rules_score/test/BUILD | 455 ------------------ bazel/rules/rules_score/test/MODULE.bazel | 83 ---- .../rules_score/test/check_unknown_needs.sh | 29 -- .../rules_score/test/fixtures/mock_lib1.cc | 8 - .../rules_score/test/fixtures/mock_lib2.cc | 8 - .../rules_score/test/fixtures/mock_test.sh | 18 - .../test/fixtures/module_a/index.rst | 44 -- .../test/fixtures/module_b/index.rst | 49 -- .../test/fixtures/module_c/index.rst | 42 -- .../seooc_test/architectural_design.rst | 187 ------- .../fixtures/seooc_test/asr_fixtures.trlc | 22 - .../seooc_test/assumptions_of_use.rst | 93 ---- .../seooc_test/comp_req_fixtures.trlc | 22 - .../seooc_test/component_requirements.rst | 118 ----- .../seooc_test/dependability_analysis.rst | 305 ------------ .../test/fixtures/seooc_test/dfa.rst | 149 ------ .../seooc_test/dynamic_architecture.rst | 66 --- .../seooc_test/feat_req_fixtures.trlc | 22 - .../seooc_test/feature_requirements.rst | 48 -- .../seooc_test/static_architecture.rst | 45 -- .../test/fixtures/test_component_main.cc | 17 - .../test/fixtures/test_unit_test.cc | 29 -- .../rules_score/test/html_generation_test.bzl | 223 --------- .../test/score_module_providers_test.bzl | 323 ------------- bazel/rules/rules_score/test/seooc_test.bzl | 147 ------ .../test/template/conf.template.py | 72 --- bazel/rules/rules_score/test/test/BUILD | 455 ------------------ .../test/test/fixtures/mock_lib1.cc | 4 - .../test/test/fixtures/mock_lib2.cc | 4 - .../test/test/fixtures/mock_test.sh | 18 - .../test/test/fixtures/module_a/index.rst | 31 -- .../test/test/fixtures/module_b/index.rst | 37 -- .../test/test/fixtures/module_c/index.rst | 29 -- .../seooc_test/architectural_design.rst | 174 ------- .../fixtures/seooc_test/asr_fixtures.trlc | 22 - .../seooc_test/assumptions_of_use.rst | 80 --- .../seooc_test/comp_req_fixtures.trlc | 22 - .../seooc_test/component_requirements.rst | 105 ---- .../seooc_test/dependability_analysis.rst | 292 ----------- .../test/test/fixtures/seooc_test/dfa.rst | 149 ------ .../seooc_test/dynamic_architecture.rst | 66 --- .../seooc_test/feat_req_fixtures.trlc | 22 - .../seooc_test/feature_requirements.rst | 48 -- .../seooc_test/static_architecture.rst | 45 -- .../test/fixtures/test_binary_unit_test.cc | 16 - .../test/test/fixtures/test_component_main.cc | 13 - .../test/test/fixtures/test_unit2_test.cc | 16 - .../test/test/fixtures/test_unit_test.cc | 16 - .../test/test/html_generation_test.bzl | 223 --------- .../test/test/score_module_providers_test.bzl | 323 ------------- .../rules_score/test/test/seooc_test.bzl | 135 ------ .../test/test/test_safety_analysis_tools.py | 359 -------------- .../test/test/unit_component_test.bzl | 181 ------- .../rules_score/test/unit_component_test.bzl | 175 ------- 57 files changed, 5697 deletions(-) delete mode 100644 bazel/rules/rules_score/test/.bazelrc delete mode 100644 bazel/rules/rules_score/test/.bazelversion delete mode 100644 bazel/rules/rules_score/test/BUILD delete mode 100644 bazel/rules/rules_score/test/MODULE.bazel delete mode 100644 bazel/rules/rules_score/test/check_unknown_needs.sh delete mode 100644 bazel/rules/rules_score/test/fixtures/mock_lib1.cc delete mode 100644 bazel/rules/rules_score/test/fixtures/mock_lib2.cc delete mode 100644 bazel/rules/rules_score/test/fixtures/mock_test.sh delete mode 100644 bazel/rules/rules_score/test/fixtures/module_a/index.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/module_b/index.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/module_c/index.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/architectural_design.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/asr_fixtures.trlc delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/assumptions_of_use.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/comp_req_fixtures.trlc delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/component_requirements.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/dependability_analysis.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/dfa.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/dynamic_architecture.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/feat_req_fixtures.trlc delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/feature_requirements.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/seooc_test/static_architecture.rst delete mode 100644 bazel/rules/rules_score/test/fixtures/test_component_main.cc delete mode 100644 bazel/rules/rules_score/test/fixtures/test_unit_test.cc delete mode 100644 bazel/rules/rules_score/test/html_generation_test.bzl delete mode 100644 bazel/rules/rules_score/test/score_module_providers_test.bzl delete mode 100644 bazel/rules/rules_score/test/seooc_test.bzl delete mode 100644 bazel/rules/rules_score/test/template/conf.template.py delete mode 100644 bazel/rules/rules_score/test/test/BUILD delete mode 100644 bazel/rules/rules_score/test/test/fixtures/mock_lib1.cc delete mode 100644 bazel/rules/rules_score/test/test/fixtures/mock_lib2.cc delete mode 100755 bazel/rules/rules_score/test/test/fixtures/mock_test.sh delete mode 100644 bazel/rules/rules_score/test/test/fixtures/module_a/index.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/module_b/index.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/module_c/index.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/architectural_design.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/asr_fixtures.trlc delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/assumptions_of_use.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/comp_req_fixtures.trlc delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/component_requirements.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/dependability_analysis.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/dfa.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/dynamic_architecture.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/feat_req_fixtures.trlc delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/feature_requirements.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/seooc_test/static_architecture.rst delete mode 100644 bazel/rules/rules_score/test/test/fixtures/test_binary_unit_test.cc delete mode 100644 bazel/rules/rules_score/test/test/fixtures/test_component_main.cc delete mode 100644 bazel/rules/rules_score/test/test/fixtures/test_unit2_test.cc delete mode 100644 bazel/rules/rules_score/test/test/fixtures/test_unit_test.cc delete mode 100644 bazel/rules/rules_score/test/test/html_generation_test.bzl delete mode 100644 bazel/rules/rules_score/test/test/score_module_providers_test.bzl delete mode 100644 bazel/rules/rules_score/test/test/seooc_test.bzl delete mode 100644 bazel/rules/rules_score/test/test/test_safety_analysis_tools.py delete mode 100644 bazel/rules/rules_score/test/test/unit_component_test.bzl delete mode 100644 bazel/rules/rules_score/test/unit_component_test.bzl diff --git a/bazel/rules/rules_score/BUILD b/bazel/rules/rules_score/BUILD index ed463fcc..ac6ca464 100644 --- a/bazel/rules/rules_score/BUILD +++ b/bazel/rules/rules_score/BUILD @@ -176,7 +176,6 @@ lobster_linker( srcs = [ ":bzl_srcs", "//bazel/rules/rules_score/private:bzl_srcs", - "//bazel/rules/rules_score/test:bzl_srcs", ], visibility = ["//docs/processes:__pkg__"], ) diff --git a/bazel/rules/rules_score/test/.bazelrc b/bazel/rules/rules_score/test/.bazelrc deleted file mode 100644 index 0bac34ab..00000000 --- a/bazel/rules/rules_score/test/.bazelrc +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# SPDX-License-Identifier: Apache-2.0 - -common --registry=https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/ -common --registry=https://bcr.bazel.build - -build --java_language_version=17 -build --tool_java_language_version=17 -build --java_runtime_version=remotejdk_17 -build --tool_java_runtime_version=remotejdk_17 diff --git a/bazel/rules/rules_score/test/.bazelversion b/bazel/rules/rules_score/test/.bazelversion deleted file mode 100644 index 56b6be4e..00000000 --- a/bazel/rules/rules_score/test/.bazelversion +++ /dev/null @@ -1 +0,0 @@ -8.3.1 diff --git a/bazel/rules/rules_score/test/BUILD b/bazel/rules/rules_score/test/BUILD deleted file mode 100644 index f1c26eda..00000000 --- a/bazel/rules/rules_score/test/BUILD +++ /dev/null @@ -1,455 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* - -load("@rules_python//python:defs.bzl", "py_test") -load("@trlc//:trlc.bzl", "trlc_requirements") -load( - "//bazel/rules/rules_score:rules_score.bzl", - "architectural_design", - "assumptions_of_use", - "component", - "component_requirements", - "dependability_analysis", - "dependable_element", - "feature_requirements", - "fmea", - "sphinx_module", - "unit", - "unit_design", -) -load( - ":html_generation_test.bzl", - "html_merging_test", - "module_dependencies_test", - "needs_transitive_test", - "sphinx_module_test_suite", -) -load( - ":seooc_test.bzl", - "seooc_artifacts_copied_test", - "seooc_needs_provider_test", - "seooc_sphinx_module_generated_test", -) -load( - ":unit_component_test.bzl", - "component_provider_test", - "component_sphinx_sources_test", - "unit_component_test_suite", - "unit_provider_test", - "unit_sphinx_sources_test", -) - -package(default_visibility = ["//visibility:public"]) - -filegroup( - name = "bzl_srcs", - srcs = glob(["*.bzl"]), - visibility = ["//bazel/rules/rules_score:__pkg__"], -) - -# ============================================================================ -# Test Fixtures - Module Definitions -# ============================================================================ - -# Test 1: Multi-Module Aggregation -# Dependency graph: module_a_lib -> module_b_lib -> module_c_lib -# module_a_lib -> module_c_lib (also direct) -sphinx_module( - name = "module_c_lib", - srcs = glob(["fixtures/module_c/*.rst"]), - index = "fixtures/module_c/index.rst", - sphinx = "//bazel/rules/rules_score:score_build", -) - -sphinx_module( - name = "module_b_lib", - srcs = glob(["fixtures/module_b/*.rst"]), - index = "fixtures/module_b/index.rst", - sphinx = "//bazel/rules/rules_score:score_build", - deps = [":module_c_lib"], -) - -sphinx_module( - name = "module_a_lib", - srcs = glob(["fixtures/module_a/*.rst"]), - index = "fixtures/module_a/index.rst", - sphinx = "//bazel/rules/rules_score:score_build", - deps = [ - ":module_b_lib", - ":module_c_lib", - ], -) - -# Test 2: SEooC (Safety Element out of Context) Module -# Tests the score_component macro with S-CORE process artifacts - -# - Feature Requirements: wp__requirements_feat -# TODO: Feature requirements are a stand-alone artifact for now -# We have to link them manually to component requirements -trlc_requirements( - name = "asr_fixtures", - srcs = ["fixtures/seooc_test/asr_fixtures.trlc"], - spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], -) - -trlc_requirements( - name = "feat_req_trlc", - srcs = ["fixtures/seooc_test/feat_req_fixtures.trlc"], - spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], - deps = [":asr_fixtures"], -) - -feature_requirements( - name = "feat_req", - srcs = [":feat_req_trlc"], -) - -# - Component Requirements: wp__requirements_comp -trlc_requirements( - name = "comp_req_trlc", - srcs = ["fixtures/seooc_test/comp_req_fixtures.trlc"], - spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], - deps = [":feat_req_trlc"], -) - -component_requirements( - name = "comp_req", - srcs = [":comp_req_trlc"], -) - -# - Assumptions of Use: wp__requirements_comp_aou -assumptions_of_use( - name = "aous", - srcs = ["fixtures/seooc_test/assumptions_of_use.rst"], - requirements = [":feat_req"], -) - -# - Architecture Design: wp__component_arch -architectural_design( - name = "arch_design", - dynamic = ["fixtures/seooc_test/dynamic_architecture.rst"], - static = ["fixtures/seooc_test/static_architecture.rst"], -) - -unit_design( - name = "unit_arch_design", - static = ["fixtures/seooc_test/static_architecture.rst"], -) - -# - Safety Analysis (DFA): wp__sw_component_dfa -# - Safety Analysis (FMEA): wp__sw_component_fmea -dependability_analysis( - name = "dependability_analysis_target", - arch_design = ":arch_design", - dfa = ["fixtures/seooc_test/dfa.rst"], - fmea = [":samplelibrary_fmea"], -) - -fmea( - name = "samplelibrary_fmea", - # TODO - # controlmeasures = [], # can be AoUs or requirements - # failuremodes = [], - # fta = [], - arch_design = ":arch_design", -) - -dependable_element( - name = "seooc_test_lib", - architectural_design = [":arch_design"], - assumptions_of_use = [":aous"], - components = [], - dependability_analysis = [":dependability_analysis_target"], - description = "Test SEooC module demonstrating S-CORE process compliance structure.", - integrity_level = "B", - requirements = [":feat_req"], - tests = [], -) - -# ============================================================================ -# Test Fixtures - Unit, Component, and Dependable Element -# ============================================================================ - -# Mock implementation targets with dummy functions -cc_library( - name = "mock_lib1", - srcs = ["fixtures/mock_lib1.cc"], -) - -cc_library( - name = "mock_lib2", - srcs = ["fixtures/mock_lib2.cc"], -) - -cc_binary( - name = "test_component_binary", - srcs = ["fixtures/test_component_main.cc"], - deps = [ - ":mock_lib1", - ":mock_lib2", - ], -) - -cc_test( - name = "test_unit_tests", - testonly = True, - srcs = ["fixtures/test_unit_test.cc"], - tags = ["manual"], - deps = [ - ":mock_lib1", - ":mock_lib2", - "@googletest//:gtest_main", - ], -) - -# Test Unit -unit( - name = "test_unit", - testonly = True, - scope = ["//bazel/rules/rules_score/test:__pkg__"], - tests = [":test_unit_tests"], - unit_design = [":unit_arch_design"], - implementation = [ - ":mock_lib1", - ":mock_lib2", - ], -) - -cc_test( - name = "test_binary_unit_tests", - testonly = True, - srcs = ["fixtures/test_binary_unit_test.cc"], - tags = ["manual"], - deps = [ - ":mock_lib1", - ":mock_lib2", - "@googletest//:gtest_main", - ], -) - -unit( - name = "test_binary_unit", - testonly = True, - scope = ["//bazel/rules/rules_score/test:__pkg__"], - tests = [":test_binary_unit_tests"], - unit_design = [":unit_arch_design"], - implementation = [":test_component_binary"], -) - -# Test Component -component( - name = "test_component", - testonly = True, - components = [ - ":test_unit", - "test_binary_unit", - ], - requirements = [":comp_req"], - tests = [], # Empty for testing -) - -# Test Dependable Element -dependable_element( - name = "test_dependable_element", - testonly = True, - architectural_design = [":arch_design"], - assumptions_of_use = [":aous"], - components = [":test_component"], - dependability_analysis = [":dependability_analysis_target"], - description = "Test dependable element for unit testing", - integrity_level = "B", - requirements = [":feat_req"], - tests = [], # Empty for testing -) - -# ============================================================================ -# Test Fixtures - Nested Components for Recursive Testing -# ============================================================================ - -# Additional mock implementations -cc_library( - name = "mock_lib3", - srcs = ["fixtures/mock_lib1.cc"], # Reuse same source for testing -) - -cc_test( - name = "test_unit2_tests", - testonly = True, - srcs = ["fixtures/test_unit2_test.cc"], - tags = ["manual"], - deps = [ - ":mock_lib2", - ":mock_lib3", - "@googletest//:gtest_main", - ], -) - -# Second unit that will be shared between components -unit( - name = "test_unit2", - testonly = True, - scope = ["//bazel/rules/rules_score/test:__pkg__"], - tests = [":test_unit2_tests"], - unit_design = [":unit_arch_design"], - implementation = [":mock_lib3"], -) - -# Nested component containing unit2 -component( - name = "test_nested_component", - testonly = True, - components = [":test_unit2"], - requirements = [":comp_req"], - tests = [], -) - -# Parent component containing nested component and shared unit -component( - name = "test_parent_component", - testonly = True, - components = [ - ":test_nested_component", - ":test_unit", # Different unit - ], - requirements = [":comp_req"], - tests = [], -) - -# Dependable element with nested components to test recursive collection -dependable_element( - name = "test_dependable_element_nested", - testonly = True, - architectural_design = [":arch_design"], - assumptions_of_use = [":aous"], - components = [":test_parent_component"], - dependability_analysis = [":dependability_analysis_target"], - description = "Test dependable element with nested components for testing recursive unit collection and deduplication", - integrity_level = "B", - requirements = [":feat_req"], - tests = [], -) - -# ============================================================================ -# Test Instantiations - HTML Generation Tests -# ============================================================================ - -# Needs Generation Tests -needs_transitive_test( - name = "needs_transitive_test", - target_under_test = ":module_b_lib_needs", -) - -# Dependency Tests -module_dependencies_test( - name = "module_dependencies_test", - target_under_test = ":module_a_lib", -) - -html_merging_test( - name = "html_merging_test", - target_under_test = ":module_a_lib", -) - -# ============================================================================ -# SEooC-Specific Tests -# ============================================================================ - -# Test that all artifacts are copied -seooc_artifacts_copied_test( - name = "seooc_tests_artifacts_copied", - target_under_test = ":seooc_test_lib_index", -) - -# Test that sphinx_module is generated with correct providers -seooc_sphinx_module_generated_test( - name = "seooc_tests_sphinx_module_generated", - target_under_test = ":seooc_test_lib", -) - -# Test that needs provider exists for cross-referencing -seooc_needs_provider_test( - name = "seooc_tests_needs_provider", - target_under_test = ":seooc_test_lib_doc_needs", -) - -# ============================================================================ -# Test Suites -# ============================================================================ - -# Main test suite combining all sphinx_module tests -sphinx_module_test_suite(name = "sphinx_module_tests") - -# SEooC-focused test suite -test_suite( - name = "seooc_tests", - tests = [ - ":seooc_tests_artifacts_copied", - ":seooc_tests_needs_provider", - ":seooc_tests_sphinx_module_generated", - ], -) - -# ============================================================================ -# Unit, Component, and Dependable Element Test Instantiations -# ============================================================================ - -# Unit tests -unit_provider_test( - name = "unit_provider_test", - target_under_test = ":test_unit", -) - -unit_sphinx_sources_test( - name = "unit_sphinx_sources_test", - target_under_test = ":test_unit", -) - -# Component tests -component_provider_test( - name = "component_provider_test", - target_under_test = ":test_component", -) - -component_sphinx_sources_test( - name = "component_sphinx_sources_test", - target_under_test = ":test_component", -) - -# Unit, Component, and Dependable Element test suite -unit_component_test_suite(name = "unit_component_tests") - -# ============================================================================ -# FTA Linker Unit Tests -# ============================================================================ - -py_test( - name = "test_safety_analysis_tools", - size = "small", - srcs = ["test_safety_analysis_tools.py"], - deps = ["//bazel/rules/rules_score:safety_analysis_tools"], -) - -# ============================================================================ -# Combined Test Suite -# ============================================================================ - -# Combined test suite for all tests -test_suite( - name = "all_tests", - tests = [ - ":seooc_tests", - ":sphinx_module_tests", - ":test_safety_analysis_tools", - ":unit_component_tests", - ], -) diff --git a/bazel/rules/rules_score/test/MODULE.bazel b/bazel/rules/rules_score/test/MODULE.bazel deleted file mode 100644 index f89cf6ff..00000000 --- a/bazel/rules/rules_score/test/MODULE.bazel +++ /dev/null @@ -1,83 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* - -module( - name = "rules_score_test", - version = "0.0.0", - compatibility_level = 1, -) - -############################################################################### -# Core Dependencies -############################################################################### -bazel_dep(name = "rules_python", version = "1.4.1") -bazel_dep(name = "rules_cc", version = "0.2.17") -bazel_dep(name = "aspect_rules_py", version = "1.4.0") -bazel_dep(name = "aspect_rules_lint", version = "1.5.3") -bazel_dep(name = "rules_shell", version = "0.5.0") -bazel_dep(name = "bazel_skylib", version = "1.7.1") -bazel_dep(name = "platforms", version = "1.0.0") - -############################################################################### -# Python Toolchain -############################################################################### -PYTHON_VERSION = "3.12" - -python = use_extension("@rules_python//python/extensions:python.bzl", "python") -python.toolchain( - is_default = True, - python_version = PYTHON_VERSION, -) - -############################################################################### -# Pip Hub -############################################################################### -pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") - -# Main tooling pip -pip.parse( - envsubst = ["PIP_INDEX_URL"], - extra_pip_args = ["--index-url=${PIP_INDEX_URL:-https://pypi.org/simple/}"], - hub_name = "pip_tooling_test", - python_version = PYTHON_VERSION, - requirements_lock = "//:requirements.txt", -) -use_repo(pip, "pip_tooling") - -bazel_dep(name = "score_docs_as_code", version = "3.0.1", dev_dependency = True) -# bazel_dep(name = "score_platform", version = "0.5.0") -# bazel_dep(name = "score_process", version = "1.3.2") - -bazel_dep(name = "score_tooling", version = "1.3.2") -local_path_override( - module_name = "score_tooling", - path = "../../../..", -) - -register_toolchains( - "//:score_toolchain", -) - -bazel_dep(name = "trlc", version = "0.0.0") -git_override( - module_name = "trlc", - commit = "c7750ebd0520996e05b9a9bc91eb6a4f62030f22", - remote = "https://github.com/bmw-software-engineering/trlc.git", -) - -bazel_dep(name = "lobster", version = "0.0.0") -git_override( - module_name = "lobster", - commit = "56881461f9d3fde2918d1731aa5937aaf64cd67c", - remote = "https://github.com/bmw-software-engineering/lobster.git", -) diff --git a/bazel/rules/rules_score/test/check_unknown_needs.sh b/bazel/rules/rules_score/test/check_unknown_needs.sh deleted file mode 100644 index 4c33dda0..00000000 --- a/bazel/rules/rules_score/test/check_unknown_needs.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# ******************************************************************************* -# 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 -# ******************************************************************************* -set -euo pipefail - -# Test if the html output contains unknown needs -html_file="./module_a_lib/html/index.html" - -if [[ ! -f "$html_file" ]]; then - echo "Error: File not found: $html_file" >&2 - exit 1 -fi - -if grep -q "Unknown need" "$html_file"; then - echo "Error: Found 'Unknown need' in $html_file" >&2 - exit 1 -fi - -echo "✓ No unknown needs found" diff --git a/bazel/rules/rules_score/test/fixtures/mock_lib1.cc b/bazel/rules/rules_score/test/fixtures/mock_lib1.cc deleted file mode 100644 index 1b677240..00000000 --- a/bazel/rules/rules_score/test/fixtures/mock_lib1.cc +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2026 Contributors to the Eclipse Foundation -// -// SPDX-License-Identifier: Apache-2.0 - -// Mock implementation for testing purposes -int mock_function_1() { - return 42; -} diff --git a/bazel/rules/rules_score/test/fixtures/mock_lib2.cc b/bazel/rules/rules_score/test/fixtures/mock_lib2.cc deleted file mode 100644 index 9fd7d511..00000000 --- a/bazel/rules/rules_score/test/fixtures/mock_lib2.cc +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2026 Contributors to the Eclipse Foundation -// -// SPDX-License-Identifier: Apache-2.0 - -// Mock implementation for testing purposes -int mock_function_2() { - return 84; -} diff --git a/bazel/rules/rules_score/test/fixtures/mock_test.sh b/bazel/rules/rules_score/test/fixtures/mock_test.sh deleted file mode 100644 index d5aa21e6..00000000 --- a/bazel/rules/rules_score/test/fixtures/mock_test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* - -# Mock test script that always succeeds -# Used for testing rule structure without actual implementation - -exit 0 diff --git a/bazel/rules/rules_score/test/fixtures/module_a/index.rst b/bazel/rules/rules_score/test/fixtures/module_a/index.rst deleted file mode 100644 index 9525277e..00000000 --- a/bazel/rules/rules_score/test/fixtures/module_a/index.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. - # ******************************************************************************* - # 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 - # ******************************************************************************* -Module A Documentation -====================== - -This is the documentation for Module A. - -.. document:: Documentation for Module A - :id: doc__module_fixtures_module_a - :status: valid - :safety: ASIL_B - :security: NO - :realizes: wp__component_arch - -Overview --------- - -Module A is a simple module that depends on Module C. - -Features --------- - -.. needlist:: - :tags: module_a - -Cross-Module References ------------------------ - -General reference to Module C :external+module_c_lib:doc:`index`. - -Need reference to Module C :need:`doc__module_fixtures_module_c`. - -Need reference to Module B :need:`doc__module_fixtures_module_b`. diff --git a/bazel/rules/rules_score/test/fixtures/module_b/index.rst b/bazel/rules/rules_score/test/fixtures/module_b/index.rst deleted file mode 100644 index eb4187e3..00000000 --- a/bazel/rules/rules_score/test/fixtures/module_b/index.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. - # ******************************************************************************* - # 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 - # ******************************************************************************* -Module B Documentation -====================== - -This is the documentation for Module B. - -.. document:: Documentation for Module B - :id: doc__module_fixtures_module_b - :status: valid - :safety: ASIL_B - :security: NO - :realizes: - -Overview --------- - -Module B depends on both Module A and Module C. - -Features --------- - -.. needlist:: - :tags: module_b - -Cross-Module References ------------------------ - -This module references: - -* :external+module_a_lib:doc:`index` from Module A -* :external+module_c_lib:doc:`index` from Module C -* Need reference to Module C :need:`doc__module_fixtures_module_c` - -Dependencies ------------- - -Module B integrates functionality from both dependent modules. diff --git a/bazel/rules/rules_score/test/fixtures/module_c/index.rst b/bazel/rules/rules_score/test/fixtures/module_c/index.rst deleted file mode 100644 index 4268a9df..00000000 --- a/bazel/rules/rules_score/test/fixtures/module_c/index.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. - # ******************************************************************************* - # 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 - # ******************************************************************************* -Module C Documentation -====================== - -This is the documentation for Module C. - -.. document:: Documentation for Module C - :id: doc__module_fixtures_module_c - :status: valid - :safety: ASIL_B - :security: NO - :realizes: - - -Overview --------- - -Module C is a base module with no dependencies. -Local need link: :need:`doc__module_fixtures_module_c` - -Features --------- - -.. needlist:: - :tags: module_c - -Content -------- - -Module C provides foundational functionality used by other modules. diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/architectural_design.rst b/bazel/rules/rules_score/test/fixtures/seooc_test/architectural_design.rst deleted file mode 100644 index 1770fe6b..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/architectural_design.rst +++ /dev/null @@ -1,187 +0,0 @@ -.. - # ******************************************************************************* - # 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 - # ******************************************************************************* -Architectural Design -==================== - -This document describes the architectural design of the test SEooC module. - -Software Architecture Overview -------------------------------- - -The system consists of the following software components: - -.. comp_arc_sta:: Input Processing Module - :id: comp_arc_sta__seooc_test__input_processing_module - :status: valid - :tags: architecture, component, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__input_data_processing, comp_req__seooc_test__can_message_reception - - Responsible for receiving and validating input data from CAN interface. - - **Inputs**: Raw CAN messages - - **Outputs**: Validated data structures - - **Safety Mechanisms**: CRC validation, sequence counter check - -.. comp_arc_sta:: Data Processing Engine - :id: comp_arc_sta__seooc_test__data_processing_engine - :status: valid - :tags: architecture, component, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__output_accuracy, comp_req__seooc_test__redundant_calculation - - Core processing component that performs calculations on validated data. - - **Inputs**: Validated data from Input Processing Module - - **Outputs**: Processed results - - **Safety Mechanisms**: Dual-channel redundant calculation - -.. comp_arc_sta:: Output Handler - :id: comp_arc_sta__seooc_test__output_handler - :status: valid - :tags: architecture, component, seooc_test - :safety: QM - :security: NO - :fulfils: comp_req__seooc_test__can_message_transmission - - Formats and transmits output data via CAN interface. - - **Inputs**: Processed results from Data Processing Engine - - **Outputs**: CAN messages - - **Safety Mechanisms**: Message sequence numbering, alive counter - -.. comp_arc_sta:: Fault Detection and Handling - :id: comp_arc_sta__seooc_test__fault_detection_handling - :status: valid - :tags: architecture, component, safety, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__fault_detection, comp_req__seooc_test__safe_state_transition - - Monitors system health and handles fault conditions. - - **Inputs**: Status from all components - - **Outputs**: System state, error flags - - **Safety Mechanisms**: Watchdog timer, plausibility checks - -Component Interfaces ---------------------- - -Interface: CAN Communication -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. real_arc_int:: CAN RX Interface - :id: real_arc_int__seooc_test__can_rx - :status: valid - :tags: interface, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__can_message_reception - :language: cpp - - * **Protocol**: CAN 2.0B - * **Baud Rate**: 500 kbps - * **Message ID Range**: 0x100-0x1FF - * **DLC**: 8 bytes - -.. real_arc_int:: CAN TX Interface - :id: real_arc_int__seooc_test__can_tx - :status: valid - :tags: interface, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__can_message_transmission - :language: cpp - - * **Protocol**: CAN 2.0B - * **Baud Rate**: 500 kbps - * **Message ID Range**: 0x200-0x2FF - * **DLC**: 8 bytes - -Design Decisions ----------------- - -.. comp_arc_dyn:: Use of Hardware Watchdog - :id: comp_arc_dyn__seooc_test__hw_watchdog - :status: valid - :tags: design-decision, safety, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__fault_detection - - The architecture includes a hardware watchdog timer to ensure system - reliability and meet safety requirements. - - **Rationale**: Hardware watchdog provides independent monitoring - of software execution and can detect timing violations. - - **Alternatives Considered**: Software-only monitoring (rejected due - to lower ASIL coverage) - -.. comp_arc_dyn:: Redundant Processing Paths - :id: comp_arc_dyn__seooc_test__redundancy - :status: valid - :tags: design-decision, safety, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__redundant_calculation - - Critical calculations are performed using redundant processing paths - to detect and prevent silent data corruption. - - **Rationale**: Meets ASIL-B requirements for detection of random - hardware faults during calculation. - - **Implementation**: Main path + shadow path with result comparison - -Memory Architecture -------------------- - -.. comp_arc_sta:: RAM Allocation - :id: comp_arc_sta__seooc_test__ram_allocation - :status: valid - :tags: resource, memory, seooc_test - :safety: QM - :security: NO - :fulfils: aou_req__seooc_test__memory_requirements - - * **Total RAM**: 512 KB - * **Stack**: 64 KB - * **Heap**: 128 KB - * **Static Data**: 256 KB - * **Reserved**: 64 KB - -.. comp_arc_sta:: Flash Allocation - :id: comp_arc_sta__seooc_test__flash_allocation - :status: valid - :tags: resource, memory, seooc_test - :safety: QM - :security: NO - :fulfils: aou_req__seooc_test__memory_requirements - - * **Total Flash**: 2 MB - * **Application Code**: 1.5 MB - * **Configuration Data**: 256 KB - * **Boot Loader**: 128 KB - * **Reserved**: 128 KB diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/asr_fixtures.trlc b/bazel/rules/rules_score/test/fixtures/seooc_test/asr_fixtures.trlc deleted file mode 100644 index 2a08c183..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/asr_fixtures.trlc +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025 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 - ********************************************************************************/ -package SeoocTest - -import ScoreReq - -ScoreReq.AssumedSystemReq ASR_001 { - description = "The system shall process data" - safety = ScoreReq.Asil.QM - rationale = "Required for basic functionality" - version = 1 -} diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/assumptions_of_use.rst b/bazel/rules/rules_score/test/fixtures/seooc_test/assumptions_of_use.rst deleted file mode 100644 index 6e95110e..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/assumptions_of_use.rst +++ /dev/null @@ -1,93 +0,0 @@ -.. - # ******************************************************************************* - # 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 - # ******************************************************************************* -Assumptions of Use -================== - -This document describes the assumptions of use for the test SEooC module. - -.. aou_req:: Operating Temperature Range - :id: aou_req__seooc_test__operating_temperature_range - :status: valid - :tags: environment, iso26262, seooc_test - :safety: ASIL_B - :security: NO - - The SEooC shall operate within temperature range -40°C to +85°C. - -.. aou_req:: Supply Voltage - :id: aou_req__seooc_test__supply_voltage - :status: valid - :tags: power, iso26262, seooc_test - :safety: ASIL_B - :security: NO - - The SEooC shall operate with supply voltage 12V ±10%. - - Maximum current consumption: 2.5A - -.. aou_req:: Processing Load - :id: aou_req__seooc_test__processing_load - :status: valid - :tags: performance, iso26262, seooc_test - :safety: ASIL_B - :security: NO - - The maximum processing load shall not exceed 80% to ensure - timing requirements are met. - -Environmental Assumptions -------------------------- - -.. aou_req:: Controlled Environment - :id: aou_req__seooc_test__controlled_environment - :status: valid - :tags: environment, seooc_test - :safety: ASIL_B - :security: NO - - The system operates in a controlled automotive environment - compliant with ISO 16750 standards. - -.. aou_req:: Maintenance - :id: aou_req__seooc_test__maintenance - :status: valid - :tags: maintenance, seooc_test - :safety: ASIL_B - :security: NO - - Regular maintenance is performed according to the maintenance - schedule defined in the integration manual. - -Integration Constraints ------------------------ - -.. aou_req:: CAN Bus Interface - :id: aou_req__seooc_test__can_bus_interface - :status: valid - :tags: interface, communication, seooc_test - :safety: ASIL_B - :security: NO - - The host system shall provide a CAN 2.0B compliant interface - for communication with the SEooC. - -.. aou_req:: Memory Requirements - :id: aou_req__seooc_test__memory_requirements - :status: valid - :tags: resource, seooc_test - :safety: ASIL_B - :security: NO - - The host system shall provide at least 512KB of RAM and - 2MB of flash memory for the SEooC. diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/comp_req_fixtures.trlc b/bazel/rules/rules_score/test/fixtures/seooc_test/comp_req_fixtures.trlc deleted file mode 100644 index 97be39b5..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/comp_req_fixtures.trlc +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025 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 - ********************************************************************************/ -package SeoocTest - -import ScoreReq - -ScoreReq.CompReq COMP_001 { - description = "The component shall process input data within defined latency bounds" - safety = ScoreReq.Asil.QM - derived_from = [SeoocTest.FEAT_001@1] - version = 1 -} diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/component_requirements.rst b/bazel/rules/rules_score/test/fixtures/seooc_test/component_requirements.rst deleted file mode 100644 index 6f6561be..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/component_requirements.rst +++ /dev/null @@ -1,118 +0,0 @@ -.. - # ******************************************************************************* - # 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 - # ******************************************************************************* -Component Requirements -====================== - -This document defines the functional and safety requirements. - -Functional Requirements ------------------------- - -.. comp_req:: Input Data Processing - :id: comp_req__seooc_test__input_data_processing - :status: valid - :tags: functional, performance, seooc_test - :safety: QM - :security: NO - :satisfies: aou_req__seooc_test__processing_load - - The system shall process input data within 100ms from reception. - - **Rationale**: Real-time processing required for control loop. - -.. comp_req:: Output Accuracy - :id: comp_req__seooc_test__output_accuracy - :status: valid - :tags: functional, quality, seooc_test - :safety: QM - :security: NO - - The system shall provide output with 99.9% accuracy under - nominal operating conditions. - -.. comp_req:: Data Logging - :id: comp_req__seooc_test__data_logging - :status: valid - :tags: functional, diagnostic, seooc_test - :safety: QM - :security: NO - - The system shall log all error events with timestamp and - error code to non-volatile memory. - -Safety Requirements -------------------- - -.. comp_req:: Fault Detection - :id: comp_req__seooc_test__fault_detection - :status: valid - :tags: safety, seooc_test - :safety: ASIL_B - :security: NO - :satisfies: aou_req__seooc_test__processing_load - - The system shall detect and handle fault conditions within 50ms. - - **ASIL Level**: ASIL-B - **Safety Mechanism**: Watchdog timer + plausibility checks - -.. comp_req:: Safe State Transition - :id: comp_req__seooc_test__safe_state_transition - :status: valid - :tags: safety, seooc_test - :safety: ASIL_B - :security: NO - - The system shall maintain safe state during power loss and - complete shutdown within 20ms. - - **ASIL Level**: ASIL-B - **Safe State**: All outputs disabled, error flag set - -.. comp_req:: Redundant Calculation - :id: comp_req__seooc_test__redundant_calculation - :status: valid - :tags: safety, seooc_test - :safety: ASIL_B - :security: NO - - Critical calculations shall be performed using redundant - processing paths with comparison. - - **ASIL Level**: ASIL-B - **Safety Mechanism**: Dual-channel processing - -Communication Requirements ---------------------------- - -.. comp_req:: CAN Message Transmission - :id: comp_req__seooc_test__can_message_transmission - :status: valid - :tags: functional, communication, seooc_test - :safety: QM - :security: NO - :satisfies: aou_req__seooc_test__can_bus_interface - - The system shall transmit status messages on CAN bus - every 100ms ±10ms. - -.. comp_req:: CAN Message Reception - :id: comp_req__seooc_test__can_message_reception - :status: valid - :tags: functional, communication, seooc_test - :safety: QM - :security: NO - :satisfies: aou_req__seooc_test__can_bus_interface - - The system shall process received CAN messages within 10ms. diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/dependability_analysis.rst b/bazel/rules/rules_score/test/fixtures/seooc_test/dependability_analysis.rst deleted file mode 100644 index 403f28c3..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/dependability_analysis.rst +++ /dev/null @@ -1,305 +0,0 @@ -.. - # ******************************************************************************* - # 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 Analysis -=============== - -This document contains the safety analysis for the test SEooC module. - -Failure Mode and Effects Analysis (FMEA) ------------------------------------------ - -.. comp_saf_fmea:: Input Data Corruption - :id: comp_saf_fmea__seooc_test__input_data_corruption - :status: valid - :tags: fmea, safety, seooc_test - :violates: comp_arc_sta__seooc_test__input_processing_module - :fault_id: bit_flip - :failure_effect: Corrupted input data from CAN bus due to electromagnetic interference, transmission errors, or faulty sensor leading to incorrect processing results - :mitigated_by: comp_req__seooc_test__fault_detection - :sufficient: yes - - **Failure Mode**: Corrupted input data from CAN bus - - **Potential Causes**: - - * Electromagnetic interference - * Transmission errors - * Faulty sensor - - **Effects**: Incorrect processing results, potential unsafe output - - **Severity**: High (S9) - - **Occurrence**: Medium (O4) - - **Detection**: High (D2) - - **RPN**: 72 - - **Detection Method**: CRC checksum validation, sequence counter check - - **Mitigation**: Reject invalid data and enter safe state within 50ms - -.. comp_saf_fmea:: Processing Timeout - :id: comp_saf_fmea__seooc_test__processing_timeout - :status: valid - :tags: fmea, safety, seooc_test - :violates: comp_arc_sta__seooc_test__fault_detection_handling - :fault_id: timing_failure - :failure_effect: Processing exceeds time deadline due to software defect, CPU overload, or hardware fault causing system unresponsiveness - :mitigated_by: comp_req__seooc_test__fault_detection - :sufficient: yes - - **Failure Mode**: Processing exceeds time deadline - - **Potential Causes**: - - * Software defect (infinite loop) - * CPU overload - * Hardware fault - - **Effects**: System becomes unresponsive, watchdog reset - - **Severity**: Medium (S6) - - **Occurrence**: Low (O3) - - **Detection**: Very High (D1) - - **RPN**: 18 - - **Detection Method**: Hardware watchdog timer - - **Mitigation**: System reset and recovery to safe state - -.. comp_saf_fmea:: Calculation Error - :id: comp_saf_fmea__seooc_test__calculation_error - :status: valid - :tags: fmea, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :fault_id: seu - :failure_effect: Incorrect calculation result due to single event upset, register corruption, or ALU malfunction - :mitigated_by: comp_req__seooc_test__redundant_calculation - :sufficient: yes - - **Failure Mode**: Incorrect calculation result due to random hardware fault - - **Potential Causes**: - - * Single event upset (SEU) - * Register corruption - * ALU malfunction - - **Effects**: Incorrect output values - - **Severity**: High (S8) - - **Occurrence**: Very Low (O2) - - **Detection**: High (D2) - - **RPN**: 32 - - **Detection Method**: Dual-channel redundant calculation with comparison - - **Mitigation**: Discard result and use previous valid value, set error flag - -Dependent Failure Analysis (DFA) ---------------------------------- - -.. comp_saf_dfa:: System Failure Top Event - :id: comp_saf_dfa__seooc_test__system_failure_top - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: common_cause - :failure_effect: System provides unsafe output due to common cause failures affecting multiple safety mechanisms simultaneously - :mitigated_by: aou_req__seooc_test__controlled_environment - :sufficient: yes - - **Top Event**: System provides unsafe output - - **Goal**: Probability < 1e-6 per hour (ASIL-B target) - -.. comp_saf_dfa:: Hardware Failure Branch - :id: comp_saf_dfa__seooc_test__hw_failure - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: hw_common_mode - :failure_effect: Hardware component failures due to common cause (overvoltage, overtemperature) affecting multiple components - :mitigated_by: aou_req__seooc_test__operating_temperature_range, aou_req__seooc_test__supply_voltage - :sufficient: yes - - **Event**: Hardware component failure - - **Sub-events**: - - * Microcontroller failure (λ = 5e-7) - * Power supply failure (λ = 3e-7) - * CAN transceiver failure (λ = 2e-7) - - **Combined Probability**: 1.0e-6 per hour - -.. comp_saf_dfa:: Software Failure Branch - :id: comp_saf_dfa__seooc_test__sw_failure - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: sw_systematic - :failure_effect: Software defect affecting both processing channels due to systematic fault in common code base - :mitigated_by: comp_req__seooc_test__redundant_calculation - :sufficient: yes - - **Event**: Software defect leads to unsafe output - - **Sub-events**: - - * Undetected software bug (λ = 8e-6, detection coverage 90%) - * Memory corruption (λ = 1e-7) - - **Combined Probability**: 9e-7 per hour (after detection coverage) - -.. comp_saf_dfa:: External Interference Branch - :id: comp_saf_dfa__seooc_test__ext_interference - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__input_processing_module - :failure_id: emi - :failure_effect: External interference causing simultaneous malfunction of multiple components - :mitigated_by: aou_req__seooc_test__controlled_environment - :sufficient: yes - - **Event**: External interference causes malfunction - - **Sub-events**: - - * EMI beyond specification (λ = 5e-8) - * Voltage transient (λ = 2e-8, mitigation 99%) - - **Combined Probability**: 5.2e-8 per hour (after mitigation) - -**Total System Failure Probability**: 1.95e-6 per hour - -**ASIL-B Target**: < 1e-5 per hour ✓ **PASSED** - -Safety Mechanisms ------------------ - -.. comp_arc_sta:: SM: Input Validation - :id: comp_arc_sta__seooc_test__sm_input_validation - :status: valid - :tags: safety-mechanism, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__fault_detection - - **Description**: All input data is validated before processing - - **Checks Performed**: - - * CRC-16 checksum validation - * Message sequence counter verification - * Data range plausibility checks - - **Diagnostic Coverage**: 95% - - **Reaction**: Reject invalid data, increment error counter, use last valid value - -.. comp_arc_sta:: SM: Watchdog Timer - :id: comp_arc_sta__seooc_test__sm_watchdog - :status: valid - :tags: safety-mechanism, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__fault_detection - - **Description**: Hardware watchdog monitors software execution - - **Configuration**: - - * Timeout: 150ms - * Window watchdog: 100-140ms trigger window - * Reset delay: 10ms - - **Diagnostic Coverage**: 99% - - **Reaction**: System reset, boot to safe state - -.. comp_arc_sta:: SM: Redundant Calculation - :id: comp_arc_sta__seooc_test__sm_redundant_calc - :status: valid - :tags: safety-mechanism, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__redundant_calculation - - **Description**: Critical calculations performed in dual channels - - **Implementation**: - - * Main calculation path - * Independent shadow path - * Result comparison with tolerance check - - **Diagnostic Coverage**: 98% - - **Reaction**: On mismatch, use previous valid value, set error flag - -Safety Validation Results --------------------------- - -.. comp_arc_dyn:: Validation: FMEA Coverage - :id: comp_arc_dyn__seooc_test__val_fmea_coverage - :status: valid - :tags: validation, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__fault_detection - - **Result**: All identified failure modes have detection mechanisms - - **Coverage**: 100% of critical failure modes - - **Status**: ✓ PASSED - -.. comp_arc_dyn:: Validation: DFA Target Achievement - :id: comp_arc_dyn__seooc_test__val_dfa_target - :status: valid - :tags: validation, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__safe_state_transition - - **Result**: System failure probability 1.95e-6 per hour - - **Target**: < 1e-5 per hour (ASIL-B) - - **Margin**: 5.1x - - **Status**: ✓ PASSED - -.. comp_arc_dyn:: Validation: Safety Mechanism Effectiveness - :id: comp_arc_dyn__seooc_test__val_sm_effectiveness - :status: valid - :tags: validation, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__redundant_calculation - - **Result**: Combined diagnostic coverage 97.3% - - **Target**: > 90% (ASIL-B) - - **Status**: ✓ PASSED diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/dfa.rst b/bazel/rules/rules_score/test/fixtures/seooc_test/dfa.rst deleted file mode 100644 index 7b2e30d9..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/dfa.rst +++ /dev/null @@ -1,149 +0,0 @@ -.. - # ******************************************************************************* - # Copyright (c) 2025 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 - # ******************************************************************************* - -Dependent Failure Analysis (DFA) -================================ - -This document contains the Dependent Failure Analysis (DFA) for the test SEooC module, -following ISO 26262 requirements for analysis of dependent failures. - -Component DFA Overview ----------------------- - -The dependent failure analysis identifies and evaluates common cause failures, -cascading failures, and dependent failures that could affect the safety of the component. - -.. comp_saf_dfa:: Common Cause Failure Analysis - :id: comp_saf_dfa__seooc_test__common_cause_analysis - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: ccf_root - :failure_effect: Common cause failures affecting multiple safety mechanisms simultaneously - :mitigated_by: aou_req__seooc_test__controlled_environment - :sufficient: yes - - **Analysis Scope**: Identification of common cause failures - - **Initiators Analyzed**: - - * Environmental conditions (temperature, EMI, vibration) - * Power supply anomalies - * Manufacturing and design defects - * Maintenance-induced failures - - **Conclusion**: All identified common cause initiators have adequate mitigation measures. - -.. comp_saf_dfa:: Power Supply Dependency - :id: comp_saf_dfa__seooc_test__power_dependency - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: power_ccf - :failure_effect: Power supply failure affecting both main and redundant processing paths - :mitigated_by: aou_req__seooc_test__supply_voltage - :sufficient: yes - - **Dependent Failure**: Power supply failure - - **Affected Elements**: - - * Main processing unit - * Redundant calculation path - * Communication interface - - **Independence Measures**: - - * Voltage monitoring with independent reference - * Brownout detection circuit - * Defined safe state on power loss - - **Residual Risk**: Acceptable (< 1e-8 per hour) - -.. comp_saf_dfa:: Clock Source Dependency - :id: comp_saf_dfa__seooc_test__clock_dependency - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: clock_ccf - :failure_effect: Clock failure causing simultaneous malfunction of timing-dependent safety mechanisms - :mitigated_by: comp_req__seooc_test__fault_detection - :sufficient: yes - - **Dependent Failure**: Clock source failure - - **Affected Elements**: - - * Watchdog timer - * Communication timing - * Task scheduling - - **Independence Measures**: - - * Internal RC oscillator as backup - * Clock monitoring unit - * Frequency range checks - - **Residual Risk**: Acceptable (< 5e-9 per hour) - -.. comp_saf_dfa:: Software Design Dependency - :id: comp_saf_dfa__seooc_test__sw_design_dependency - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: sw_ccf - :failure_effect: Systematic software defect in common code base affecting both calculation paths - :mitigated_by: comp_req__seooc_test__redundant_calculation - :sufficient: yes - - **Dependent Failure**: Systematic software defect - - **Affected Elements**: - - * Main calculation algorithm - * Redundant calculation algorithm - * Result comparison logic - - **Independence Measures**: - - * Diverse implementation of redundant path - * Independent development teams - * Different compilers/toolchains for each path - - **Residual Risk**: Acceptable (< 1e-7 per hour with diversity measures) - -DFA Summary ------------ - -.. comp_saf_dfa:: DFA Summary and Conclusion - :id: comp_saf_dfa__seooc_test__dfa_summary - :status: valid - :tags: dfa, safety, seooc_test, summary - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: dfa_summary - :failure_effect: Combined dependent failure probability assessment - :mitigated_by: aou_req__seooc_test__controlled_environment - :sufficient: yes - - **Total Dependent Failure Probability**: < 1.5e-7 per hour - - **ASIL-B Target for Dependent Failures**: < 1e-6 per hour - - **Margin**: 6.7x - - **Status**: ✓ PASSED - - **Conclusion**: The component design provides adequate independence between - safety mechanisms. All identified dependent failure modes have been analyzed - and appropriate mitigation measures are in place. diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/dynamic_architecture.rst b/bazel/rules/rules_score/test/fixtures/seooc_test/dynamic_architecture.rst deleted file mode 100644 index 33cf03f5..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/dynamic_architecture.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. - # ******************************************************************************* - # Copyright (c) 2025 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 - # ******************************************************************************* - -Dynamic Architecture -==================== - -This file contains the dynamic architectural design for the SEooC test component. - -.. comp_arc_dyn:: Data Processing Sequence - :id: comp_arc_dyn__seooc_test__data_processing - :security: NO - :safety: QM - :status: valid - :fulfils: comp_req__seooc_test__input_data_processing - - Sequence diagram showing the data processing flow from input to output. - - .. uml:: - - @startuml - participant "Client" as client - participant "SEooC Test Component" as main - participant "Data Processor" as processor - - client -> main : processData(input) - main -> processor : process(input) - processor --> main : result - main --> client : output - @enduml - -.. comp_arc_dyn:: Fault Handling Sequence - :id: comp_arc_dyn__seooc_test__fault_handling - :security: NO - :safety: ASIL_B - :status: valid - :fulfils: comp_req__seooc_test__fault_detection - - Sequence diagram showing the fault detection and safe state transition. - - .. uml:: - - @startuml - participant "Main Component" as main - participant "Fault Handler" as fault - participant "Safe State Manager" as safe - - main -> fault : checkHealth() - alt fault detected - fault -> safe : transitionToSafeState() - safe --> fault : safeStateConfirmed - fault --> main : faultHandled - else no fault - fault --> main : healthOK - end - @enduml diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/feat_req_fixtures.trlc b/bazel/rules/rules_score/test/fixtures/seooc_test/feat_req_fixtures.trlc deleted file mode 100644 index a9b85aef..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/feat_req_fixtures.trlc +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025 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 - ********************************************************************************/ -package SeoocTest - -import ScoreReq - -ScoreReq.FeatReq FEAT_001 { - description = "The component shall process input data" - safety = ScoreReq.Asil.QM - derived_from = [SeoocTest.ASR_001@1] - version = 1 -} diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/feature_requirements.rst b/bazel/rules/rules_score/test/fixtures/seooc_test/feature_requirements.rst deleted file mode 100644 index d1be18a5..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/feature_requirements.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. - # ******************************************************************************* - # Copyright (c) 2025 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 - # ******************************************************************************* - -Feature Requirements -==================== - -This file contains the feature requirements for the SEooC test module. - -.. feat_req:: Data Processing - :id: feat_req__seooc_test__data_processing - :reqtype: Functional - :security: NO - :safety: QM - :satisfies: stkh_req__platform__data_handling - :status: valid - - The SEooC test component shall process input data and provide processed output. - -.. feat_req:: Safe State Management - :id: feat_req__seooc_test__safe_state - :reqtype: Functional - :security: NO - :safety: ASIL_B - :satisfies: stkh_req__platform__safety - :status: valid - - The SEooC test component shall transition to a safe state upon detection of a fault condition. - -.. feat_req:: CAN Communication - :id: feat_req__seooc_test__can_comm - :reqtype: Interface - :security: NO - :safety: QM - :satisfies: stkh_req__platform__communication - :status: valid - - The SEooC test component shall support CAN message transmission and reception. diff --git a/bazel/rules/rules_score/test/fixtures/seooc_test/static_architecture.rst b/bazel/rules/rules_score/test/fixtures/seooc_test/static_architecture.rst deleted file mode 100644 index b81321cb..00000000 --- a/bazel/rules/rules_score/test/fixtures/seooc_test/static_architecture.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. - # ******************************************************************************* - # Copyright (c) 2025 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 - # ******************************************************************************* - -Static Architecture -=================== - -This file contains the static architectural design for the SEooC test component. - -.. comp_arc_sta:: SEooC Test Component - :id: comp_arc_sta__seooc_test__main - :security: NO - :safety: QM - :status: valid - :fulfils: comp_req__seooc_test__input_data_processing - - The main component of the SEooC test module providing data processing capabilities. - -.. comp_arc_sta:: Data Processor - :id: comp_arc_sta__seooc_test__data_processor - :security: NO - :safety: QM - :status: valid - :fulfils: comp_req__seooc_test__output_accuracy - - Sub-component responsible for processing input data and generating output. - -.. comp_arc_sta:: Fault Handler - :id: comp_arc_sta__seooc_test__fault_handler - :security: NO - :safety: ASIL_B - :status: valid - :fulfils: comp_req__seooc_test__fault_detection - - Sub-component responsible for detecting and handling fault conditions. diff --git a/bazel/rules/rules_score/test/fixtures/test_component_main.cc b/bazel/rules/rules_score/test/fixtures/test_component_main.cc deleted file mode 100644 index c95cf56a..00000000 --- a/bazel/rules/rules_score/test/fixtures/test_component_main.cc +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2026 Contributors to the Eclipse Foundation -// -// SPDX-License-Identifier: Apache-2.0 - -// Main implementation for test_component -#include - -// Declarations from mock libraries -extern int mock_function_1(); -extern int mock_function_2(); - -int main(int argc, char** argv) { - std::cout << "Test Component Implementation" << std::endl; - std::cout << "Mock function 1 returns: " << mock_function_1() << std::endl; - std::cout << "Mock function 2 returns: " << mock_function_2() << std::endl; - return 0; -} diff --git a/bazel/rules/rules_score/test/fixtures/test_unit_test.cc b/bazel/rules/rules_score/test/fixtures/test_unit_test.cc deleted file mode 100644 index 416e839f..00000000 --- a/bazel/rules/rules_score/test/fixtures/test_unit_test.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2026 Contributors to the Eclipse Foundation -// -// SPDX-License-Identifier: Apache-2.0 - -// Unit tests for mock libraries -#include - -// Declarations from mock libraries -extern int mock_function_1(); -extern int mock_function_2(); - -int main() { - // Test mock_function_1 - int result1 = mock_function_1(); - if (result1 != 42) { - std::cerr << "Test failed: mock_function_1() returned " << result1 << ", expected 42" << std::endl; - return 1; - } - - // Test mock_function_2 - int result2 = mock_function_2(); - if (result2 != 84) { - std::cerr << "Test failed: mock_function_2() returned " << result2 << ", expected 84" << std::endl; - return 1; - } - - std::cout << "All tests passed!" << std::endl; - return 0; -} diff --git a/bazel/rules/rules_score/test/html_generation_test.bzl b/bazel/rules/rules_score/test/html_generation_test.bzl deleted file mode 100644 index 031bca03..00000000 --- a/bazel/rules/rules_score/test/html_generation_test.bzl +++ /dev/null @@ -1,223 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* -"""Test rules for sphinx_module HTML generation and dependencies.""" - -load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("@score_tooling//bazel/rules/rules_score:providers.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") - -# ============================================================================ -# Provider Tests -# ============================================================================ - -def _providers_test_impl(ctx): - """Test that sphinx_module provides the correct providers.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Verify required providers - asserts.true( - env, - SphinxModuleInfo in target_under_test, - "Target should provide SphinxModuleInfo", - ) - - asserts.true( - env, - DefaultInfo in target_under_test, - "Target should provide DefaultInfo", - ) - - return analysistest.end(env) - -providers_test = analysistest.make(_providers_test_impl) - -# ============================================================================ -# HTML Generation Tests -# ============================================================================ - -def _basic_html_generation_test_impl(ctx): - """Test that a simple document generates HTML output.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check that HTML directory exists - score_info = target_under_test[SphinxModuleInfo] - asserts.true( - env, - score_info.html_dir != None, - "Module should generate HTML directory", - ) - - return analysistest.end(env) - -basic_html_generation_test = analysistest.make(_basic_html_generation_test_impl) - -# ============================================================================ -# Needs.json Generation Tests -# ============================================================================ - -def _needs_generation_test_impl(ctx): - """Test that sphinx_module generates needs.json files.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check for SphinxNeedsInfo provider on _needs target - # Note: This test requires the _needs suffix target - asserts.true( - env, - DefaultInfo in target_under_test, - "Needs target should provide DefaultInfo", - ) - - return analysistest.end(env) - -needs_generation_test = analysistest.make(_needs_generation_test_impl) - -def _needs_transitive_test_impl(ctx): - """Test that needs.json files are collected transitively.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Verify SphinxNeedsInfo provider - asserts.true( - env, - SphinxNeedsInfo in target_under_test, - "Needs target should provide SphinxNeedsInfo", - ) - - needs_info = target_under_test[SphinxNeedsInfo] - - # Check direct needs.json file - asserts.true( - env, - needs_info.needs_json_file != None, - "Should have direct needs.json file", - ) - - # Check transitive needs collection - asserts.true( - env, - needs_info.needs_json_files != None, - "Should have transitive needs.json files depset", - ) - - return analysistest.end(env) - -needs_transitive_test = analysistest.make(_needs_transitive_test_impl) - -# ============================================================================ -# Dependency and Integration Tests -# ============================================================================ - -def _module_dependencies_test_impl(ctx): - """Test that module dependencies are properly handled.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module with dependencies should still generate HTML - asserts.true( - env, - score_info.html_dir != None, - "Module with dependencies should generate HTML", - ) - - return analysistest.end(env) - -module_dependencies_test = analysistest.make(_module_dependencies_test_impl) - -def _html_merging_test_impl(ctx): - """Test that HTML from dependencies is merged correctly.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Verify merged HTML output exists - asserts.true( - env, - score_info.html_dir != None, - "Merged HTML should be generated", - ) - - return analysistest.end(env) - -html_merging_test = analysistest.make(_html_merging_test_impl) - -# ============================================================================ -# Config Generation Tests -# ============================================================================ - -def _auto_config_generation_test_impl(ctx): - """Test that conf.py is automatically generated when not provided.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module without explicit config should still generate HTML - asserts.true( - env, - score_info.html_dir != None, - "Module with auto-generated config should produce HTML", - ) - - return analysistest.end(env) - -auto_config_generation_test = analysistest.make(_auto_config_generation_test_impl) - -def _explicit_config_test_impl(ctx): - """Test that explicit conf.py is used when provided.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module with explicit config should generate HTML - asserts.true( - env, - score_info.html_dir != None, - "Module with explicit config should produce HTML", - ) - - return analysistest.end(env) - -explicit_config_test = analysistest.make(_explicit_config_test_impl) - -# ============================================================================ -# Test Suite -# ============================================================================ - -def sphinx_module_test_suite(name): - """Create a comprehensive test suite for sphinx_module. - - Tests cover: - - Needs.json generation and transitive collection - - Module dependencies and HTML merging - - Args: - name: Name of the test suite - """ - - native.test_suite( - name = name, - tests = [ - # Needs generation - ":needs_transitive_test", - - # Dependencies and integration - ":module_dependencies_test", - ":html_merging_test", - ], - ) diff --git a/bazel/rules/rules_score/test/score_module_providers_test.bzl b/bazel/rules/rules_score/test/score_module_providers_test.bzl deleted file mode 100644 index 4dc9ead5..00000000 --- a/bazel/rules/rules_score/test/score_module_providers_test.bzl +++ /dev/null @@ -1,323 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* -"""Tests for sphinx_module providers and two-phase build system.""" - -load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("//bazel/rules/rules_score:providers.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") - -# ============================================================================ -# SphinxModuleInfo Provider Tests -# ============================================================================ - -def _sphinx_module_info_fields_test_impl(ctx): - """Test that SphinxModuleInfo provides all required fields.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - asserts.true( - env, - SphinxModuleInfo in target_under_test, - "Target should provide SphinxModuleInfo", - ) - - score_info = target_under_test[SphinxModuleInfo] - - # Verify html_dir field - asserts.true( - env, - hasattr(score_info, "html_dir"), - "SphinxModuleInfo should have html_dir field", - ) - - asserts.true( - env, - score_info.html_dir != None, - "html_dir should not be None", - ) - - return analysistest.end(env) - -sphinx_module_info_fields_test = analysistest.make(_sphinx_module_info_fields_test_impl) - -# ============================================================================ -# SphinxNeedsInfo Provider Tests -# ============================================================================ - -def _score_needs_info_fields_test_impl(ctx): - """Test that SphinxNeedsInfo provides all required fields.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - asserts.true( - env, - SphinxNeedsInfo in target_under_test, - "Needs target should provide SphinxNeedsInfo", - ) - - needs_info = target_under_test[SphinxNeedsInfo] - - # Verify needs_json_file field (direct file) - asserts.true( - env, - hasattr(needs_info, "needs_json_file"), - "SphinxNeedsInfo should have needs_json_file field", - ) - - asserts.true( - env, - needs_info.needs_json_file != None, - "needs_json_file should not be None", - ) - - # Verify needs_json_files field (transitive depset) - asserts.true( - env, - hasattr(needs_info, "needs_json_files"), - "SphinxNeedsInfo should have needs_json_files field", - ) - - asserts.true( - env, - needs_info.needs_json_files != None, - "needs_json_files should not be None", - ) - - # Verify it's a depset - asserts.true( - env, - type(needs_info.needs_json_files) == type(depset([])), - "needs_json_files should be a depset", - ) - - return analysistest.end(env) - -score_needs_info_fields_test = analysistest.make(_score_needs_info_fields_test_impl) - -def _score_needs_transitive_collection_test_impl(ctx): - """Test that needs.json files are collected transitively.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - needs_info = target_under_test[SphinxNeedsInfo] - - # Get the list of transitive needs files - transitive_needs = needs_info.needs_json_files.to_list() - - # Should have at least the direct needs file - asserts.true( - env, - len(transitive_needs) >= 1, - "Should have at least the direct needs.json file", - ) - - # Direct file should be in the transitive set - direct_file = needs_info.needs_json_file - asserts.true( - env, - direct_file in transitive_needs, - "Direct needs.json file should be in transitive collection", - ) - - return analysistest.end(env) - -score_needs_transitive_collection_test = analysistest.make(_score_needs_transitive_collection_test_impl) - -def _score_needs_with_deps_test_impl(ctx): - """Test that needs.json files include dependencies.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - needs_info = target_under_test[SphinxNeedsInfo] - transitive_needs = needs_info.needs_json_files.to_list() - - # Module with dependencies should have multiple needs files - # (its own + dependencies) - asserts.true( - env, - len(transitive_needs) >= 1, - "Module with dependencies should collect transitive needs.json files", - ) - - return analysistest.end(env) - -score_needs_with_deps_test = analysistest.make(_score_needs_with_deps_test_impl) - -# ============================================================================ -# Two-Phase Build Tests -# ============================================================================ - -def _two_phase_needs_first_test_impl(ctx): - """Test that Phase 1 (needs generation) works independently.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Verify SphinxNeedsInfo provider - asserts.true( - env, - SphinxNeedsInfo in target_under_test, - "Phase 1 should provide SphinxNeedsInfo", - ) - - # Verify DefaultInfo with needs.json output - asserts.true( - env, - DefaultInfo in target_under_test, - "Phase 1 should provide DefaultInfo", - ) - - default_info = target_under_test[DefaultInfo] - files = default_info.files.to_list() - - # Should have at least one file (needs.json) - asserts.true( - env, - len(files) >= 1, - "Phase 1 should output needs.json file", - ) - - return analysistest.end(env) - -two_phase_needs_first_test = analysistest.make(_two_phase_needs_first_test_impl) - -def _two_phase_html_second_test_impl(ctx): - """Test that Phase 2 (HTML generation) works with needs from Phase 1.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Verify SphinxModuleInfo provider - asserts.true( - env, - SphinxModuleInfo in target_under_test, - "Phase 2 should provide SphinxModuleInfo", - ) - - score_info = target_under_test[SphinxModuleInfo] - - # Verify HTML output - asserts.true( - env, - score_info.html_dir != None, - "Phase 2 should generate HTML directory", - ) - - return analysistest.end(env) - -two_phase_html_second_test = analysistest.make(_two_phase_html_second_test_impl) - -# ============================================================================ -# Config Generation Tests -# ============================================================================ - -def _config_auto_generation_test_impl(ctx): - """Test that conf.py is auto-generated when not provided.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module without explicit config should still build - asserts.true( - env, - score_info.html_dir != None, - "Auto-generated config should allow HTML generation", - ) - - return analysistest.end(env) - -config_auto_generation_test = analysistest.make(_config_auto_generation_test_impl) - -def _config_explicit_usage_test_impl(ctx): - """Test that explicit conf.py is used when provided.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module with explicit config should build - asserts.true( - env, - score_info.html_dir != None, - "Explicit config should allow HTML generation", - ) - - return analysistest.end(env) - -config_explicit_usage_test = analysistest.make(_config_explicit_usage_test_impl) - -# ============================================================================ -# Dependency Handling Tests -# ============================================================================ - -def _deps_html_merging_test_impl(ctx): - """Test that HTML from dependencies is merged into output.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module with dependencies should generate merged HTML - asserts.true( - env, - score_info.html_dir != None, - "Module with dependencies should generate merged HTML", - ) - - return analysistest.end(env) - -deps_html_merging_test = analysistest.make(_deps_html_merging_test_impl) - -def _deps_needs_collection_test_impl(ctx): - """Test that needs from dependencies are collected.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - needs_info = target_under_test[SphinxNeedsInfo] - transitive_needs = needs_info.needs_json_files.to_list() - - # Should collect needs from dependencies - asserts.true( - env, - len(transitive_needs) >= 1, - "Should collect needs.json from dependencies", - ) - - return analysistest.end(env) - -deps_needs_collection_test = analysistest.make(_deps_needs_collection_test_impl) - -# ============================================================================ -# Test Suite -# ============================================================================ - -def sphinx_module_providers_test_suite(name): - """Create a test suite for sphinx_module providers and build phases. - - Tests cover: - - Transitive needs.json collection - - Dependency handling (HTML merging, needs collection) - - Args: - name: Name of the test suite - """ - - native.test_suite( - name = name, - tests = [ - # Provider tests - ":score_needs_with_deps_test", - - # Dependency tests - ":deps_html_merging_test", - ":deps_needs_collection_test", - ], - ) diff --git a/bazel/rules/rules_score/test/seooc_test.bzl b/bazel/rules/rules_score/test/seooc_test.bzl deleted file mode 100644 index b823bc84..00000000 --- a/bazel/rules/rules_score/test/seooc_test.bzl +++ /dev/null @@ -1,147 +0,0 @@ -# ******************************************************************************* -# 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 -# ******************************************************************************* -""" -Test suite for dependable_element macro. - -Tests the SEooC (Safety Element out of Context) functionality including: -- Index generation with artifact references -- Integration with sphinx_module -- Sphinx-needs cross-referencing -- HTML output generation -""" - -load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("@score_tooling//bazel/rules/rules_score:providers.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") - -def _seooc_index_generation_test_impl(ctx): - """Test that dependable_element generates proper index.rst file.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Get the generated index file - files = target_under_test[DefaultInfo].files.to_list() - - # Find index.rst in the output files - index_file = None - for f in files: - if f.basename == "index.rst": - index_file = f - break - - # Assert index file exists - asserts.true( - env, - index_file != None, - "Expected index.rst to be generated by dependable_element_index rule", - ) - - return analysistest.end(env) - -seooc_index_generation_test = analysistest.make( - impl = _seooc_index_generation_test_impl, -) - -def _seooc_artifacts_copied_test_impl(ctx): - """Test that all dependable element artifacts are copied to output directory.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - files = target_under_test[DefaultInfo].files.to_list() - - # Expected artifact basenames - these come from the SphinxSourcesInfo providers - # and are filtered to only include .rst/.md files for the index - expected_artifacts = [ - "component_requirements.rst", # from requirements - "dfa.rst", # from :dependability_analysis_target - ] - - # Check each artifact exists - actual_basenames = [f.basename for f in files] - for artifact in expected_artifacts: - asserts.true( - env, - artifact in actual_basenames, - "Expected artifact '{}' to be in output files".format(artifact), - ) - - return analysistest.end(env) - -seooc_artifacts_copied_test = analysistest.make( - impl = _seooc_artifacts_copied_test_impl, -) - -def _seooc_sphinx_module_generated_test_impl(ctx): - """Test that dependable_element generates sphinx_module with HTML output.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check that SphinxModuleInfo provider exists - asserts.true( - env, - SphinxModuleInfo in target_under_test, - "Expected dependable_element to provide SphinxModuleInfo from sphinx_module", - ) - - return analysistest.end(env) - -seooc_sphinx_module_generated_test = analysistest.make( - impl = _seooc_sphinx_module_generated_test_impl, -) - -def _seooc_needs_provider_test_impl(ctx): - """Test that dependable_element generates needs provider for cross-referencing.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check that SphinxNeedsInfo provider exists - asserts.true( - env, - SphinxNeedsInfo in target_under_test, - "Expected dependable_element_needs to provide SphinxNeedsInfo", - ) - - return analysistest.end(env) - -seooc_needs_provider_test = analysistest.make( - impl = _seooc_needs_provider_test_impl, -) - -def _seooc_description_test_impl(ctx): - """Test that SEooC includes description in generated index.rst.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Get the generated index file - files = target_under_test[DefaultInfo].files.to_list() - - # Find index.rst - index_file = None - for f in files: - if f.basename == "index.rst": - index_file = f - break - - # Note: We can't easily read file contents in analysis test, - # but we can verify the file exists. The description content - # would be validated through integration tests or manual inspection. - asserts.true( - env, - index_file != None, - "Expected index.rst to exist for description validation", - ) - - return analysistest.end(env) - -seooc_description_test = analysistest.make( - impl = _seooc_description_test_impl, -) diff --git a/bazel/rules/rules_score/test/template/conf.template.py b/bazel/rules/rules_score/test/template/conf.template.py deleted file mode 100644 index b8670fa6..00000000 --- a/bazel/rules/rules_score/test/template/conf.template.py +++ /dev/null @@ -1,72 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* - -""" -Generic Sphinx configuration template for SCORE modules. - -This file is auto-generated from a template and should not be edited directly. -Template variables like {PROJECT_NAME} are replaced during Bazel build. -""" - -import bazel_sphinx_needs - -# Project configuration - {PROJECT_NAME} will be replaced by the module name during build -project = "{PROJECT_NAME}" -author = "S-CORE" -version = "1.0" -release = "1.0.0" -project_url = ( - "https://github.com/eclipse-score" # Required by score_metamodel extension -) - -# Sphinx extensions - comprehensive list for SCORE modules -extensions = [ - "sphinx_needs", - "sphinx_design", - "myst_parser", - "sphinxcontrib.plantuml", - "score_plantuml", - "score_metamodel", - "score_draw_uml_funcs", - "score_source_code_linker", - "score_layout", -] - -# MyST parser extensions -myst_enable_extensions = ["colon_fence"] - -# Exclude patterns for Bazel builds -exclude_patterns = [ - "bazel-*", - ".venv*", -] - -# Enable markdown rendering -source_suffix = { - ".rst": "restructuredtext", - ".md": "markdown", -} - -# Enable numref for cross-references -numfig = True - -# HTML theme -html_theme = "sphinx_rtd_theme" - -# Load external needs and log configuration -needs_external_needs = bazel_sphinx_needs.load_external_needs() -bazel_sphinx_needs.log_config_info(project) - - -def setup(app): - return bazel_sphinx_needs.setup_sphinx_extension(app, needs_external_needs) diff --git a/bazel/rules/rules_score/test/test/BUILD b/bazel/rules/rules_score/test/test/BUILD deleted file mode 100644 index f1c26eda..00000000 --- a/bazel/rules/rules_score/test/test/BUILD +++ /dev/null @@ -1,455 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* - -load("@rules_python//python:defs.bzl", "py_test") -load("@trlc//:trlc.bzl", "trlc_requirements") -load( - "//bazel/rules/rules_score:rules_score.bzl", - "architectural_design", - "assumptions_of_use", - "component", - "component_requirements", - "dependability_analysis", - "dependable_element", - "feature_requirements", - "fmea", - "sphinx_module", - "unit", - "unit_design", -) -load( - ":html_generation_test.bzl", - "html_merging_test", - "module_dependencies_test", - "needs_transitive_test", - "sphinx_module_test_suite", -) -load( - ":seooc_test.bzl", - "seooc_artifacts_copied_test", - "seooc_needs_provider_test", - "seooc_sphinx_module_generated_test", -) -load( - ":unit_component_test.bzl", - "component_provider_test", - "component_sphinx_sources_test", - "unit_component_test_suite", - "unit_provider_test", - "unit_sphinx_sources_test", -) - -package(default_visibility = ["//visibility:public"]) - -filegroup( - name = "bzl_srcs", - srcs = glob(["*.bzl"]), - visibility = ["//bazel/rules/rules_score:__pkg__"], -) - -# ============================================================================ -# Test Fixtures - Module Definitions -# ============================================================================ - -# Test 1: Multi-Module Aggregation -# Dependency graph: module_a_lib -> module_b_lib -> module_c_lib -# module_a_lib -> module_c_lib (also direct) -sphinx_module( - name = "module_c_lib", - srcs = glob(["fixtures/module_c/*.rst"]), - index = "fixtures/module_c/index.rst", - sphinx = "//bazel/rules/rules_score:score_build", -) - -sphinx_module( - name = "module_b_lib", - srcs = glob(["fixtures/module_b/*.rst"]), - index = "fixtures/module_b/index.rst", - sphinx = "//bazel/rules/rules_score:score_build", - deps = [":module_c_lib"], -) - -sphinx_module( - name = "module_a_lib", - srcs = glob(["fixtures/module_a/*.rst"]), - index = "fixtures/module_a/index.rst", - sphinx = "//bazel/rules/rules_score:score_build", - deps = [ - ":module_b_lib", - ":module_c_lib", - ], -) - -# Test 2: SEooC (Safety Element out of Context) Module -# Tests the score_component macro with S-CORE process artifacts - -# - Feature Requirements: wp__requirements_feat -# TODO: Feature requirements are a stand-alone artifact for now -# We have to link them manually to component requirements -trlc_requirements( - name = "asr_fixtures", - srcs = ["fixtures/seooc_test/asr_fixtures.trlc"], - spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], -) - -trlc_requirements( - name = "feat_req_trlc", - srcs = ["fixtures/seooc_test/feat_req_fixtures.trlc"], - spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], - deps = [":asr_fixtures"], -) - -feature_requirements( - name = "feat_req", - srcs = [":feat_req_trlc"], -) - -# - Component Requirements: wp__requirements_comp -trlc_requirements( - name = "comp_req_trlc", - srcs = ["fixtures/seooc_test/comp_req_fixtures.trlc"], - spec = ["@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model"], - deps = [":feat_req_trlc"], -) - -component_requirements( - name = "comp_req", - srcs = [":comp_req_trlc"], -) - -# - Assumptions of Use: wp__requirements_comp_aou -assumptions_of_use( - name = "aous", - srcs = ["fixtures/seooc_test/assumptions_of_use.rst"], - requirements = [":feat_req"], -) - -# - Architecture Design: wp__component_arch -architectural_design( - name = "arch_design", - dynamic = ["fixtures/seooc_test/dynamic_architecture.rst"], - static = ["fixtures/seooc_test/static_architecture.rst"], -) - -unit_design( - name = "unit_arch_design", - static = ["fixtures/seooc_test/static_architecture.rst"], -) - -# - Safety Analysis (DFA): wp__sw_component_dfa -# - Safety Analysis (FMEA): wp__sw_component_fmea -dependability_analysis( - name = "dependability_analysis_target", - arch_design = ":arch_design", - dfa = ["fixtures/seooc_test/dfa.rst"], - fmea = [":samplelibrary_fmea"], -) - -fmea( - name = "samplelibrary_fmea", - # TODO - # controlmeasures = [], # can be AoUs or requirements - # failuremodes = [], - # fta = [], - arch_design = ":arch_design", -) - -dependable_element( - name = "seooc_test_lib", - architectural_design = [":arch_design"], - assumptions_of_use = [":aous"], - components = [], - dependability_analysis = [":dependability_analysis_target"], - description = "Test SEooC module demonstrating S-CORE process compliance structure.", - integrity_level = "B", - requirements = [":feat_req"], - tests = [], -) - -# ============================================================================ -# Test Fixtures - Unit, Component, and Dependable Element -# ============================================================================ - -# Mock implementation targets with dummy functions -cc_library( - name = "mock_lib1", - srcs = ["fixtures/mock_lib1.cc"], -) - -cc_library( - name = "mock_lib2", - srcs = ["fixtures/mock_lib2.cc"], -) - -cc_binary( - name = "test_component_binary", - srcs = ["fixtures/test_component_main.cc"], - deps = [ - ":mock_lib1", - ":mock_lib2", - ], -) - -cc_test( - name = "test_unit_tests", - testonly = True, - srcs = ["fixtures/test_unit_test.cc"], - tags = ["manual"], - deps = [ - ":mock_lib1", - ":mock_lib2", - "@googletest//:gtest_main", - ], -) - -# Test Unit -unit( - name = "test_unit", - testonly = True, - scope = ["//bazel/rules/rules_score/test:__pkg__"], - tests = [":test_unit_tests"], - unit_design = [":unit_arch_design"], - implementation = [ - ":mock_lib1", - ":mock_lib2", - ], -) - -cc_test( - name = "test_binary_unit_tests", - testonly = True, - srcs = ["fixtures/test_binary_unit_test.cc"], - tags = ["manual"], - deps = [ - ":mock_lib1", - ":mock_lib2", - "@googletest//:gtest_main", - ], -) - -unit( - name = "test_binary_unit", - testonly = True, - scope = ["//bazel/rules/rules_score/test:__pkg__"], - tests = [":test_binary_unit_tests"], - unit_design = [":unit_arch_design"], - implementation = [":test_component_binary"], -) - -# Test Component -component( - name = "test_component", - testonly = True, - components = [ - ":test_unit", - "test_binary_unit", - ], - requirements = [":comp_req"], - tests = [], # Empty for testing -) - -# Test Dependable Element -dependable_element( - name = "test_dependable_element", - testonly = True, - architectural_design = [":arch_design"], - assumptions_of_use = [":aous"], - components = [":test_component"], - dependability_analysis = [":dependability_analysis_target"], - description = "Test dependable element for unit testing", - integrity_level = "B", - requirements = [":feat_req"], - tests = [], # Empty for testing -) - -# ============================================================================ -# Test Fixtures - Nested Components for Recursive Testing -# ============================================================================ - -# Additional mock implementations -cc_library( - name = "mock_lib3", - srcs = ["fixtures/mock_lib1.cc"], # Reuse same source for testing -) - -cc_test( - name = "test_unit2_tests", - testonly = True, - srcs = ["fixtures/test_unit2_test.cc"], - tags = ["manual"], - deps = [ - ":mock_lib2", - ":mock_lib3", - "@googletest//:gtest_main", - ], -) - -# Second unit that will be shared between components -unit( - name = "test_unit2", - testonly = True, - scope = ["//bazel/rules/rules_score/test:__pkg__"], - tests = [":test_unit2_tests"], - unit_design = [":unit_arch_design"], - implementation = [":mock_lib3"], -) - -# Nested component containing unit2 -component( - name = "test_nested_component", - testonly = True, - components = [":test_unit2"], - requirements = [":comp_req"], - tests = [], -) - -# Parent component containing nested component and shared unit -component( - name = "test_parent_component", - testonly = True, - components = [ - ":test_nested_component", - ":test_unit", # Different unit - ], - requirements = [":comp_req"], - tests = [], -) - -# Dependable element with nested components to test recursive collection -dependable_element( - name = "test_dependable_element_nested", - testonly = True, - architectural_design = [":arch_design"], - assumptions_of_use = [":aous"], - components = [":test_parent_component"], - dependability_analysis = [":dependability_analysis_target"], - description = "Test dependable element with nested components for testing recursive unit collection and deduplication", - integrity_level = "B", - requirements = [":feat_req"], - tests = [], -) - -# ============================================================================ -# Test Instantiations - HTML Generation Tests -# ============================================================================ - -# Needs Generation Tests -needs_transitive_test( - name = "needs_transitive_test", - target_under_test = ":module_b_lib_needs", -) - -# Dependency Tests -module_dependencies_test( - name = "module_dependencies_test", - target_under_test = ":module_a_lib", -) - -html_merging_test( - name = "html_merging_test", - target_under_test = ":module_a_lib", -) - -# ============================================================================ -# SEooC-Specific Tests -# ============================================================================ - -# Test that all artifacts are copied -seooc_artifacts_copied_test( - name = "seooc_tests_artifacts_copied", - target_under_test = ":seooc_test_lib_index", -) - -# Test that sphinx_module is generated with correct providers -seooc_sphinx_module_generated_test( - name = "seooc_tests_sphinx_module_generated", - target_under_test = ":seooc_test_lib", -) - -# Test that needs provider exists for cross-referencing -seooc_needs_provider_test( - name = "seooc_tests_needs_provider", - target_under_test = ":seooc_test_lib_doc_needs", -) - -# ============================================================================ -# Test Suites -# ============================================================================ - -# Main test suite combining all sphinx_module tests -sphinx_module_test_suite(name = "sphinx_module_tests") - -# SEooC-focused test suite -test_suite( - name = "seooc_tests", - tests = [ - ":seooc_tests_artifacts_copied", - ":seooc_tests_needs_provider", - ":seooc_tests_sphinx_module_generated", - ], -) - -# ============================================================================ -# Unit, Component, and Dependable Element Test Instantiations -# ============================================================================ - -# Unit tests -unit_provider_test( - name = "unit_provider_test", - target_under_test = ":test_unit", -) - -unit_sphinx_sources_test( - name = "unit_sphinx_sources_test", - target_under_test = ":test_unit", -) - -# Component tests -component_provider_test( - name = "component_provider_test", - target_under_test = ":test_component", -) - -component_sphinx_sources_test( - name = "component_sphinx_sources_test", - target_under_test = ":test_component", -) - -# Unit, Component, and Dependable Element test suite -unit_component_test_suite(name = "unit_component_tests") - -# ============================================================================ -# FTA Linker Unit Tests -# ============================================================================ - -py_test( - name = "test_safety_analysis_tools", - size = "small", - srcs = ["test_safety_analysis_tools.py"], - deps = ["//bazel/rules/rules_score:safety_analysis_tools"], -) - -# ============================================================================ -# Combined Test Suite -# ============================================================================ - -# Combined test suite for all tests -test_suite( - name = "all_tests", - tests = [ - ":seooc_tests", - ":sphinx_module_tests", - ":test_safety_analysis_tools", - ":unit_component_tests", - ], -) diff --git a/bazel/rules/rules_score/test/test/fixtures/mock_lib1.cc b/bazel/rules/rules_score/test/test/fixtures/mock_lib1.cc deleted file mode 100644 index 599e3c18..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/mock_lib1.cc +++ /dev/null @@ -1,4 +0,0 @@ -// Mock implementation for testing purposes -int mock_function_1() { - return 42; -} diff --git a/bazel/rules/rules_score/test/test/fixtures/mock_lib2.cc b/bazel/rules/rules_score/test/test/fixtures/mock_lib2.cc deleted file mode 100644 index 588f15da..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/mock_lib2.cc +++ /dev/null @@ -1,4 +0,0 @@ -// Mock implementation for testing purposes -int mock_function_2() { - return 84; -} diff --git a/bazel/rules/rules_score/test/test/fixtures/mock_test.sh b/bazel/rules/rules_score/test/test/fixtures/mock_test.sh deleted file mode 100755 index d5aa21e6..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/mock_test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* - -# Mock test script that always succeeds -# Used for testing rule structure without actual implementation - -exit 0 diff --git a/bazel/rules/rules_score/test/test/fixtures/module_a/index.rst b/bazel/rules/rules_score/test/test/fixtures/module_a/index.rst deleted file mode 100644 index 573ad4bd..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/module_a/index.rst +++ /dev/null @@ -1,31 +0,0 @@ -Module A Documentation -====================== - -This is the documentation for Module A. - -.. document:: Documentation for Module A - :id: doc__module_fixtures_module_a - :status: valid - :safety: ASIL_B - :security: NO - :realizes: wp__component_arch - -Overview --------- - -Module A is a simple module that depends on Module C. - -Features --------- - -.. needlist:: - :tags: module_a - -Cross-Module References ------------------------ - -General reference to Module C :external+module_c_lib:doc:`index`. - -Need reference to Module C :need:`doc__module_fixtures_module_c`. - -Need reference to Module B :need:`doc__module_fixtures_module_b`. diff --git a/bazel/rules/rules_score/test/test/fixtures/module_b/index.rst b/bazel/rules/rules_score/test/test/fixtures/module_b/index.rst deleted file mode 100644 index 3155c10d..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/module_b/index.rst +++ /dev/null @@ -1,37 +0,0 @@ -Module B Documentation -====================== - -This is the documentation for Module B. - -.. document:: Documentation for Module B - :id: doc__module_fixtures_module_b - :status: valid - :safety: ASIL_B - :security: NO - :realizes: - -Overview --------- - -Module B depends on both Module A and Module C. - -Features --------- - -.. needlist:: - :tags: module_b - -Cross-Module References ------------------------ - -This module references: - -* :external+module_a_lib:doc:`index` from Module A -* :external+module_c_lib:doc:`index` from Module C -* Need reference to Module C :need:`doc__module_fixtures_module_c` -* Need reference to Module C :need:`doc__module_fixtures_module_d` - -Dependencies ------------- - -Module B integrates functionality from both dependent modules. diff --git a/bazel/rules/rules_score/test/test/fixtures/module_c/index.rst b/bazel/rules/rules_score/test/test/fixtures/module_c/index.rst deleted file mode 100644 index b73ae61f..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/module_c/index.rst +++ /dev/null @@ -1,29 +0,0 @@ -Module C Documentation -====================== - -This is the documentation for Module C. - -.. document:: Documentation for Module C - :id: doc__module_fixtures_module_c - :status: valid - :safety: ASIL_B - :security: NO - :realizes: - - -Overview --------- - -Module C is a base module with no dependencies. -Local need link: :need:`doc__module_fixtures_module_c` - -Features --------- - -.. needlist:: - :tags: module_c - -Content -------- - -Module C provides foundational functionality used by other modules. diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/architectural_design.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/architectural_design.rst deleted file mode 100644 index 02e96f7d..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/architectural_design.rst +++ /dev/null @@ -1,174 +0,0 @@ -Architectural Design -==================== - -This document describes the architectural design of the test SEooC module. - -Software Architecture Overview -------------------------------- - -The system consists of the following software components: - -.. comp_arc_sta:: Input Processing Module - :id: comp_arc_sta__seooc_test__input_processing_module - :status: valid - :tags: architecture, component, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__input_data_processing, comp_req__seooc_test__can_message_reception - - Responsible for receiving and validating input data from CAN interface. - - **Inputs**: Raw CAN messages - - **Outputs**: Validated data structures - - **Safety Mechanisms**: CRC validation, sequence counter check - -.. comp_arc_sta:: Data Processing Engine - :id: comp_arc_sta__seooc_test__data_processing_engine - :status: valid - :tags: architecture, component, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__output_accuracy, comp_req__seooc_test__redundant_calculation - - Core processing component that performs calculations on validated data. - - **Inputs**: Validated data from Input Processing Module - - **Outputs**: Processed results - - **Safety Mechanisms**: Dual-channel redundant calculation - -.. comp_arc_sta:: Output Handler - :id: comp_arc_sta__seooc_test__output_handler - :status: valid - :tags: architecture, component, seooc_test - :safety: QM - :security: NO - :fulfils: comp_req__seooc_test__can_message_transmission - - Formats and transmits output data via CAN interface. - - **Inputs**: Processed results from Data Processing Engine - - **Outputs**: CAN messages - - **Safety Mechanisms**: Message sequence numbering, alive counter - -.. comp_arc_sta:: Fault Detection and Handling - :id: comp_arc_sta__seooc_test__fault_detection_handling - :status: valid - :tags: architecture, component, safety, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__fault_detection, comp_req__seooc_test__safe_state_transition - - Monitors system health and handles fault conditions. - - **Inputs**: Status from all components - - **Outputs**: System state, error flags - - **Safety Mechanisms**: Watchdog timer, plausibility checks - -Component Interfaces ---------------------- - -Interface: CAN Communication -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. real_arc_int:: CAN RX Interface - :id: real_arc_int__seooc_test__can_rx - :status: valid - :tags: interface, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__can_message_reception - :language: cpp - - * **Protocol**: CAN 2.0B - * **Baud Rate**: 500 kbps - * **Message ID Range**: 0x100-0x1FF - * **DLC**: 8 bytes - -.. real_arc_int:: CAN TX Interface - :id: real_arc_int__seooc_test__can_tx - :status: valid - :tags: interface, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__can_message_transmission - :language: cpp - - * **Protocol**: CAN 2.0B - * **Baud Rate**: 500 kbps - * **Message ID Range**: 0x200-0x2FF - * **DLC**: 8 bytes - -Design Decisions ----------------- - -.. comp_arc_dyn:: Use of Hardware Watchdog - :id: comp_arc_dyn__seooc_test__hw_watchdog - :status: valid - :tags: design-decision, safety, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__fault_detection - - The architecture includes a hardware watchdog timer to ensure system - reliability and meet safety requirements. - - **Rationale**: Hardware watchdog provides independent monitoring - of software execution and can detect timing violations. - - **Alternatives Considered**: Software-only monitoring (rejected due - to lower ASIL coverage) - -.. comp_arc_dyn:: Redundant Processing Paths - :id: comp_arc_dyn__seooc_test__redundancy - :status: valid - :tags: design-decision, safety, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__redundant_calculation - - Critical calculations are performed using redundant processing paths - to detect and prevent silent data corruption. - - **Rationale**: Meets ASIL-B requirements for detection of random - hardware faults during calculation. - - **Implementation**: Main path + shadow path with result comparison - -Memory Architecture -------------------- - -.. comp_arc_sta:: RAM Allocation - :id: comp_arc_sta__seooc_test__ram_allocation - :status: valid - :tags: resource, memory, seooc_test - :safety: QM - :security: NO - :fulfils: aou_req__seooc_test__memory_requirements - - * **Total RAM**: 512 KB - * **Stack**: 64 KB - * **Heap**: 128 KB - * **Static Data**: 256 KB - * **Reserved**: 64 KB - -.. comp_arc_sta:: Flash Allocation - :id: comp_arc_sta__seooc_test__flash_allocation - :status: valid - :tags: resource, memory, seooc_test - :safety: QM - :security: NO - :fulfils: aou_req__seooc_test__memory_requirements - - * **Total Flash**: 2 MB - * **Application Code**: 1.5 MB - * **Configuration Data**: 256 KB - * **Boot Loader**: 128 KB - * **Reserved**: 128 KB diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/asr_fixtures.trlc b/bazel/rules/rules_score/test/test/fixtures/seooc_test/asr_fixtures.trlc deleted file mode 100644 index 2a08c183..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/asr_fixtures.trlc +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025 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 - ********************************************************************************/ -package SeoocTest - -import ScoreReq - -ScoreReq.AssumedSystemReq ASR_001 { - description = "The system shall process data" - safety = ScoreReq.Asil.QM - rationale = "Required for basic functionality" - version = 1 -} diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/assumptions_of_use.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/assumptions_of_use.rst deleted file mode 100644 index fae172cd..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/assumptions_of_use.rst +++ /dev/null @@ -1,80 +0,0 @@ -Assumptions of Use -================== - -This document describes the assumptions of use for the test SEooC module. - -.. aou_req:: Operating Temperature Range - :id: aou_req__seooc_test__operating_temperature_range - :status: valid - :tags: environment, iso26262, seooc_test - :safety: ASIL_B - :security: NO - - The SEooC shall operate within temperature range -40°C to +85°C. - -.. aou_req:: Supply Voltage - :id: aou_req__seooc_test__supply_voltage - :status: valid - :tags: power, iso26262, seooc_test - :safety: ASIL_B - :security: NO - - The SEooC shall operate with supply voltage 12V ±10%. - - Maximum current consumption: 2.5A - -.. aou_req:: Processing Load - :id: aou_req__seooc_test__processing_load - :status: valid - :tags: performance, iso26262, seooc_test - :safety: ASIL_B - :security: NO - - The maximum processing load shall not exceed 80% to ensure - timing requirements are met. - -Environmental Assumptions -------------------------- - -.. aou_req:: Controlled Environment - :id: aou_req__seooc_test__controlled_environment - :status: valid - :tags: environment, seooc_test - :safety: ASIL_B - :security: NO - - The system operates in a controlled automotive environment - compliant with ISO 16750 standards. - -.. aou_req:: Maintenance - :id: aou_req__seooc_test__maintenance - :status: valid - :tags: maintenance, seooc_test - :safety: ASIL_B - :security: NO - - Regular maintenance is performed according to the maintenance - schedule defined in the integration manual. - -Integration Constraints ------------------------ - -.. aou_req:: CAN Bus Interface - :id: aou_req__seooc_test__can_bus_interface - :status: valid - :tags: interface, communication, seooc_test - :safety: ASIL_B - :security: NO - - The host system shall provide a CAN 2.0B compliant interface - for communication with the SEooC. - -.. aou_req:: Memory Requirements - :id: aou_req__seooc_test__memory_requirements - :status: valid - :tags: resource, seooc_test - :safety: ASIL_B - :security: NO - - The host system shall provide at least 512KB of RAM and - 2MB of flash memory for the SEooC. diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/comp_req_fixtures.trlc b/bazel/rules/rules_score/test/test/fixtures/seooc_test/comp_req_fixtures.trlc deleted file mode 100644 index 97be39b5..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/comp_req_fixtures.trlc +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025 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 - ********************************************************************************/ -package SeoocTest - -import ScoreReq - -ScoreReq.CompReq COMP_001 { - description = "The component shall process input data within defined latency bounds" - safety = ScoreReq.Asil.QM - derived_from = [SeoocTest.FEAT_001@1] - version = 1 -} diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/component_requirements.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/component_requirements.rst deleted file mode 100644 index 1d7f90c2..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/component_requirements.rst +++ /dev/null @@ -1,105 +0,0 @@ -Component Requirements -====================== - -This document defines the functional and safety requirements. - -Functional Requirements ------------------------- - -.. comp_req:: Input Data Processing - :id: comp_req__seooc_test__input_data_processing - :status: valid - :tags: functional, performance, seooc_test - :safety: QM - :security: NO - :satisfies: aou_req__seooc_test__processing_load - - The system shall process input data within 100ms from reception. - - **Rationale**: Real-time processing required for control loop. - -.. comp_req:: Output Accuracy - :id: comp_req__seooc_test__output_accuracy - :status: valid - :tags: functional, quality, seooc_test - :safety: QM - :security: NO - - The system shall provide output with 99.9% accuracy under - nominal operating conditions. - -.. comp_req:: Data Logging - :id: comp_req__seooc_test__data_logging - :status: valid - :tags: functional, diagnostic, seooc_test - :safety: QM - :security: NO - - The system shall log all error events with timestamp and - error code to non-volatile memory. - -Safety Requirements -------------------- - -.. comp_req:: Fault Detection - :id: comp_req__seooc_test__fault_detection - :status: valid - :tags: safety, seooc_test - :safety: ASIL_B - :security: NO - :satisfies: aou_req__seooc_test__processing_load - - The system shall detect and handle fault conditions within 50ms. - - **ASIL Level**: ASIL-B - **Safety Mechanism**: Watchdog timer + plausibility checks - -.. comp_req:: Safe State Transition - :id: comp_req__seooc_test__safe_state_transition - :status: valid - :tags: safety, seooc_test - :safety: ASIL_B - :security: NO - - The system shall maintain safe state during power loss and - complete shutdown within 20ms. - - **ASIL Level**: ASIL-B - **Safe State**: All outputs disabled, error flag set - -.. comp_req:: Redundant Calculation - :id: comp_req__seooc_test__redundant_calculation - :status: valid - :tags: safety, seooc_test - :safety: ASIL_B - :security: NO - - Critical calculations shall be performed using redundant - processing paths with comparison. - - **ASIL Level**: ASIL-B - **Safety Mechanism**: Dual-channel processing - -Communication Requirements ---------------------------- - -.. comp_req:: CAN Message Transmission - :id: comp_req__seooc_test__can_message_transmission - :status: valid - :tags: functional, communication, seooc_test - :safety: QM - :security: NO - :satisfies: aou_req__seooc_test__can_bus_interface - - The system shall transmit status messages on CAN bus - every 100ms ±10ms. - -.. comp_req:: CAN Message Reception - :id: comp_req__seooc_test__can_message_reception - :status: valid - :tags: functional, communication, seooc_test - :safety: QM - :security: NO - :satisfies: aou_req__seooc_test__can_bus_interface - - The system shall process received CAN messages within 10ms. diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/dependability_analysis.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/dependability_analysis.rst deleted file mode 100644 index ea5b518e..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/dependability_analysis.rst +++ /dev/null @@ -1,292 +0,0 @@ -Safety Analysis -=============== - -This document contains the safety analysis for the test SEooC module. - -Failure Mode and Effects Analysis (FMEA) ------------------------------------------ - -.. comp_saf_fmea:: Input Data Corruption - :id: comp_saf_fmea__seooc_test__input_data_corruption - :status: valid - :tags: fmea, safety, seooc_test - :violates: comp_arc_sta__seooc_test__input_processing_module - :fault_id: bit_flip - :failure_effect: Corrupted input data from CAN bus due to electromagnetic interference, transmission errors, or faulty sensor leading to incorrect processing results - :mitigated_by: comp_req__seooc_test__fault_detection - :sufficient: yes - - **Failure Mode**: Corrupted input data from CAN bus - - **Potential Causes**: - - * Electromagnetic interference - * Transmission errors - * Faulty sensor - - **Effects**: Incorrect processing results, potential unsafe output - - **Severity**: High (S9) - - **Occurrence**: Medium (O4) - - **Detection**: High (D2) - - **RPN**: 72 - - **Detection Method**: CRC checksum validation, sequence counter check - - **Mitigation**: Reject invalid data and enter safe state within 50ms - -.. comp_saf_fmea:: Processing Timeout - :id: comp_saf_fmea__seooc_test__processing_timeout - :status: valid - :tags: fmea, safety, seooc_test - :violates: comp_arc_sta__seooc_test__fault_detection_handling - :fault_id: timing_failure - :failure_effect: Processing exceeds time deadline due to software defect, CPU overload, or hardware fault causing system unresponsiveness - :mitigated_by: comp_req__seooc_test__fault_detection - :sufficient: yes - - **Failure Mode**: Processing exceeds time deadline - - **Potential Causes**: - - * Software defect (infinite loop) - * CPU overload - * Hardware fault - - **Effects**: System becomes unresponsive, watchdog reset - - **Severity**: Medium (S6) - - **Occurrence**: Low (O3) - - **Detection**: Very High (D1) - - **RPN**: 18 - - **Detection Method**: Hardware watchdog timer - - **Mitigation**: System reset and recovery to safe state - -.. comp_saf_fmea:: Calculation Error - :id: comp_saf_fmea__seooc_test__calculation_error - :status: valid - :tags: fmea, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :fault_id: seu - :failure_effect: Incorrect calculation result due to single event upset, register corruption, or ALU malfunction - :mitigated_by: comp_req__seooc_test__redundant_calculation - :sufficient: yes - - **Failure Mode**: Incorrect calculation result due to random hardware fault - - **Potential Causes**: - - * Single event upset (SEU) - * Register corruption - * ALU malfunction - - **Effects**: Incorrect output values - - **Severity**: High (S8) - - **Occurrence**: Very Low (O2) - - **Detection**: High (D2) - - **RPN**: 32 - - **Detection Method**: Dual-channel redundant calculation with comparison - - **Mitigation**: Discard result and use previous valid value, set error flag - -Dependent Failure Analysis (DFA) ---------------------------------- - -.. comp_saf_dfa:: System Failure Top Event - :id: comp_saf_dfa__seooc_test__system_failure_top - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: common_cause - :failure_effect: System provides unsafe output due to common cause failures affecting multiple safety mechanisms simultaneously - :mitigated_by: aou_req__seooc_test__controlled_environment - :sufficient: yes - - **Top Event**: System provides unsafe output - - **Goal**: Probability < 1e-6 per hour (ASIL-B target) - -.. comp_saf_dfa:: Hardware Failure Branch - :id: comp_saf_dfa__seooc_test__hw_failure - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: hw_common_mode - :failure_effect: Hardware component failures due to common cause (overvoltage, overtemperature) affecting multiple components - :mitigated_by: aou_req__seooc_test__operating_temperature_range, aou_req__seooc_test__supply_voltage - :sufficient: yes - - **Event**: Hardware component failure - - **Sub-events**: - - * Microcontroller failure (λ = 5e-7) - * Power supply failure (λ = 3e-7) - * CAN transceiver failure (λ = 2e-7) - - **Combined Probability**: 1.0e-6 per hour - -.. comp_saf_dfa:: Software Failure Branch - :id: comp_saf_dfa__seooc_test__sw_failure - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: sw_systematic - :failure_effect: Software defect affecting both processing channels due to systematic fault in common code base - :mitigated_by: comp_req__seooc_test__redundant_calculation - :sufficient: yes - - **Event**: Software defect leads to unsafe output - - **Sub-events**: - - * Undetected software bug (λ = 8e-6, detection coverage 90%) - * Memory corruption (λ = 1e-7) - - **Combined Probability**: 9e-7 per hour (after detection coverage) - -.. comp_saf_dfa:: External Interference Branch - :id: comp_saf_dfa__seooc_test__ext_interference - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__input_processing_module - :failure_id: emi - :failure_effect: External interference causing simultaneous malfunction of multiple components - :mitigated_by: aou_req__seooc_test__controlled_environment - :sufficient: yes - - **Event**: External interference causes malfunction - - **Sub-events**: - - * EMI beyond specification (λ = 5e-8) - * Voltage transient (λ = 2e-8, mitigation 99%) - - **Combined Probability**: 5.2e-8 per hour (after mitigation) - -**Total System Failure Probability**: 1.95e-6 per hour - -**ASIL-B Target**: < 1e-5 per hour ✓ **PASSED** - -Safety Mechanisms ------------------ - -.. comp_arc_sta:: SM: Input Validation - :id: comp_arc_sta__seooc_test__sm_input_validation - :status: valid - :tags: safety-mechanism, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__fault_detection - - **Description**: All input data is validated before processing - - **Checks Performed**: - - * CRC-16 checksum validation - * Message sequence counter verification - * Data range plausibility checks - - **Diagnostic Coverage**: 95% - - **Reaction**: Reject invalid data, increment error counter, use last valid value - -.. comp_arc_sta:: SM: Watchdog Timer - :id: comp_arc_sta__seooc_test__sm_watchdog - :status: valid - :tags: safety-mechanism, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__fault_detection - - **Description**: Hardware watchdog monitors software execution - - **Configuration**: - - * Timeout: 150ms - * Window watchdog: 100-140ms trigger window - * Reset delay: 10ms - - **Diagnostic Coverage**: 99% - - **Reaction**: System reset, boot to safe state - -.. comp_arc_sta:: SM: Redundant Calculation - :id: comp_arc_sta__seooc_test__sm_redundant_calc - :status: valid - :tags: safety-mechanism, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__redundant_calculation - - **Description**: Critical calculations performed in dual channels - - **Implementation**: - - * Main calculation path - * Independent shadow path - * Result comparison with tolerance check - - **Diagnostic Coverage**: 98% - - **Reaction**: On mismatch, use previous valid value, set error flag - -Safety Validation Results --------------------------- - -.. comp_arc_dyn:: Validation: FMEA Coverage - :id: comp_arc_dyn__seooc_test__val_fmea_coverage - :status: valid - :tags: validation, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__fault_detection - - **Result**: All identified failure modes have detection mechanisms - - **Coverage**: 100% of critical failure modes - - **Status**: ✓ PASSED - -.. comp_arc_dyn:: Validation: DFA Target Achievement - :id: comp_arc_dyn__seooc_test__val_dfa_target - :status: valid - :tags: validation, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__safe_state_transition - - **Result**: System failure probability 1.95e-6 per hour - - **Target**: < 1e-5 per hour (ASIL-B) - - **Margin**: 5.1x - - **Status**: ✓ PASSED - -.. comp_arc_dyn:: Validation: Safety Mechanism Effectiveness - :id: comp_arc_dyn__seooc_test__val_sm_effectiveness - :status: valid - :tags: validation, seooc_test - :safety: ASIL_B - :security: NO - :fulfils: comp_req__seooc_test__redundant_calculation - - **Result**: Combined diagnostic coverage 97.3% - - **Target**: > 90% (ASIL-B) - - **Status**: ✓ PASSED diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/dfa.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/dfa.rst deleted file mode 100644 index 7b2e30d9..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/dfa.rst +++ /dev/null @@ -1,149 +0,0 @@ -.. - # ******************************************************************************* - # Copyright (c) 2025 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 - # ******************************************************************************* - -Dependent Failure Analysis (DFA) -================================ - -This document contains the Dependent Failure Analysis (DFA) for the test SEooC module, -following ISO 26262 requirements for analysis of dependent failures. - -Component DFA Overview ----------------------- - -The dependent failure analysis identifies and evaluates common cause failures, -cascading failures, and dependent failures that could affect the safety of the component. - -.. comp_saf_dfa:: Common Cause Failure Analysis - :id: comp_saf_dfa__seooc_test__common_cause_analysis - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: ccf_root - :failure_effect: Common cause failures affecting multiple safety mechanisms simultaneously - :mitigated_by: aou_req__seooc_test__controlled_environment - :sufficient: yes - - **Analysis Scope**: Identification of common cause failures - - **Initiators Analyzed**: - - * Environmental conditions (temperature, EMI, vibration) - * Power supply anomalies - * Manufacturing and design defects - * Maintenance-induced failures - - **Conclusion**: All identified common cause initiators have adequate mitigation measures. - -.. comp_saf_dfa:: Power Supply Dependency - :id: comp_saf_dfa__seooc_test__power_dependency - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: power_ccf - :failure_effect: Power supply failure affecting both main and redundant processing paths - :mitigated_by: aou_req__seooc_test__supply_voltage - :sufficient: yes - - **Dependent Failure**: Power supply failure - - **Affected Elements**: - - * Main processing unit - * Redundant calculation path - * Communication interface - - **Independence Measures**: - - * Voltage monitoring with independent reference - * Brownout detection circuit - * Defined safe state on power loss - - **Residual Risk**: Acceptable (< 1e-8 per hour) - -.. comp_saf_dfa:: Clock Source Dependency - :id: comp_saf_dfa__seooc_test__clock_dependency - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: clock_ccf - :failure_effect: Clock failure causing simultaneous malfunction of timing-dependent safety mechanisms - :mitigated_by: comp_req__seooc_test__fault_detection - :sufficient: yes - - **Dependent Failure**: Clock source failure - - **Affected Elements**: - - * Watchdog timer - * Communication timing - * Task scheduling - - **Independence Measures**: - - * Internal RC oscillator as backup - * Clock monitoring unit - * Frequency range checks - - **Residual Risk**: Acceptable (< 5e-9 per hour) - -.. comp_saf_dfa:: Software Design Dependency - :id: comp_saf_dfa__seooc_test__sw_design_dependency - :status: valid - :tags: dfa, safety, seooc_test - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: sw_ccf - :failure_effect: Systematic software defect in common code base affecting both calculation paths - :mitigated_by: comp_req__seooc_test__redundant_calculation - :sufficient: yes - - **Dependent Failure**: Systematic software defect - - **Affected Elements**: - - * Main calculation algorithm - * Redundant calculation algorithm - * Result comparison logic - - **Independence Measures**: - - * Diverse implementation of redundant path - * Independent development teams - * Different compilers/toolchains for each path - - **Residual Risk**: Acceptable (< 1e-7 per hour with diversity measures) - -DFA Summary ------------ - -.. comp_saf_dfa:: DFA Summary and Conclusion - :id: comp_saf_dfa__seooc_test__dfa_summary - :status: valid - :tags: dfa, safety, seooc_test, summary - :violates: comp_arc_sta__seooc_test__data_processing_engine - :failure_id: dfa_summary - :failure_effect: Combined dependent failure probability assessment - :mitigated_by: aou_req__seooc_test__controlled_environment - :sufficient: yes - - **Total Dependent Failure Probability**: < 1.5e-7 per hour - - **ASIL-B Target for Dependent Failures**: < 1e-6 per hour - - **Margin**: 6.7x - - **Status**: ✓ PASSED - - **Conclusion**: The component design provides adequate independence between - safety mechanisms. All identified dependent failure modes have been analyzed - and appropriate mitigation measures are in place. diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/dynamic_architecture.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/dynamic_architecture.rst deleted file mode 100644 index 33cf03f5..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/dynamic_architecture.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. - # ******************************************************************************* - # Copyright (c) 2025 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 - # ******************************************************************************* - -Dynamic Architecture -==================== - -This file contains the dynamic architectural design for the SEooC test component. - -.. comp_arc_dyn:: Data Processing Sequence - :id: comp_arc_dyn__seooc_test__data_processing - :security: NO - :safety: QM - :status: valid - :fulfils: comp_req__seooc_test__input_data_processing - - Sequence diagram showing the data processing flow from input to output. - - .. uml:: - - @startuml - participant "Client" as client - participant "SEooC Test Component" as main - participant "Data Processor" as processor - - client -> main : processData(input) - main -> processor : process(input) - processor --> main : result - main --> client : output - @enduml - -.. comp_arc_dyn:: Fault Handling Sequence - :id: comp_arc_dyn__seooc_test__fault_handling - :security: NO - :safety: ASIL_B - :status: valid - :fulfils: comp_req__seooc_test__fault_detection - - Sequence diagram showing the fault detection and safe state transition. - - .. uml:: - - @startuml - participant "Main Component" as main - participant "Fault Handler" as fault - participant "Safe State Manager" as safe - - main -> fault : checkHealth() - alt fault detected - fault -> safe : transitionToSafeState() - safe --> fault : safeStateConfirmed - fault --> main : faultHandled - else no fault - fault --> main : healthOK - end - @enduml diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/feat_req_fixtures.trlc b/bazel/rules/rules_score/test/test/fixtures/seooc_test/feat_req_fixtures.trlc deleted file mode 100644 index a9b85aef..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/feat_req_fixtures.trlc +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025 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 - ********************************************************************************/ -package SeoocTest - -import ScoreReq - -ScoreReq.FeatReq FEAT_001 { - description = "The component shall process input data" - safety = ScoreReq.Asil.QM - derived_from = [SeoocTest.ASR_001@1] - version = 1 -} diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/feature_requirements.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/feature_requirements.rst deleted file mode 100644 index d1be18a5..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/feature_requirements.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. - # ******************************************************************************* - # Copyright (c) 2025 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 - # ******************************************************************************* - -Feature Requirements -==================== - -This file contains the feature requirements for the SEooC test module. - -.. feat_req:: Data Processing - :id: feat_req__seooc_test__data_processing - :reqtype: Functional - :security: NO - :safety: QM - :satisfies: stkh_req__platform__data_handling - :status: valid - - The SEooC test component shall process input data and provide processed output. - -.. feat_req:: Safe State Management - :id: feat_req__seooc_test__safe_state - :reqtype: Functional - :security: NO - :safety: ASIL_B - :satisfies: stkh_req__platform__safety - :status: valid - - The SEooC test component shall transition to a safe state upon detection of a fault condition. - -.. feat_req:: CAN Communication - :id: feat_req__seooc_test__can_comm - :reqtype: Interface - :security: NO - :safety: QM - :satisfies: stkh_req__platform__communication - :status: valid - - The SEooC test component shall support CAN message transmission and reception. diff --git a/bazel/rules/rules_score/test/test/fixtures/seooc_test/static_architecture.rst b/bazel/rules/rules_score/test/test/fixtures/seooc_test/static_architecture.rst deleted file mode 100644 index b81321cb..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/seooc_test/static_architecture.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. - # ******************************************************************************* - # Copyright (c) 2025 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 - # ******************************************************************************* - -Static Architecture -=================== - -This file contains the static architectural design for the SEooC test component. - -.. comp_arc_sta:: SEooC Test Component - :id: comp_arc_sta__seooc_test__main - :security: NO - :safety: QM - :status: valid - :fulfils: comp_req__seooc_test__input_data_processing - - The main component of the SEooC test module providing data processing capabilities. - -.. comp_arc_sta:: Data Processor - :id: comp_arc_sta__seooc_test__data_processor - :security: NO - :safety: QM - :status: valid - :fulfils: comp_req__seooc_test__output_accuracy - - Sub-component responsible for processing input data and generating output. - -.. comp_arc_sta:: Fault Handler - :id: comp_arc_sta__seooc_test__fault_handler - :security: NO - :safety: ASIL_B - :status: valid - :fulfils: comp_req__seooc_test__fault_detection - - Sub-component responsible for detecting and handling fault conditions. diff --git a/bazel/rules/rules_score/test/test/fixtures/test_binary_unit_test.cc b/bazel/rules/rules_score/test/test/fixtures/test_binary_unit_test.cc deleted file mode 100644 index c6db43f3..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/test_binary_unit_test.cc +++ /dev/null @@ -1,16 +0,0 @@ -// Unit tests for test_binary_unit fixture -#include - -// Declarations from mock libraries -extern int mock_function_1(); -extern int mock_function_2(); - -TEST(BinaryUnitTest, MockFunction1ReturnsExpectedValue) { - ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); - EXPECT_EQ(mock_function_1(), 42); -} - -TEST(BinaryUnitTest, MockFunction2ReturnsExpectedValue) { - ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); - EXPECT_EQ(mock_function_2(), 84); -} diff --git a/bazel/rules/rules_score/test/test/fixtures/test_component_main.cc b/bazel/rules/rules_score/test/test/fixtures/test_component_main.cc deleted file mode 100644 index 578f2d59..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/test_component_main.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Main implementation for test_component -#include - -// Declarations from mock libraries -extern int mock_function_1(); -extern int mock_function_2(); - -int main(int argc, char** argv) { - std::cout << "Test Component Implementation" << std::endl; - std::cout << "Mock function 1 returns: " << mock_function_1() << std::endl; - std::cout << "Mock function 2 returns: " << mock_function_2() << std::endl; - return 0; -} diff --git a/bazel/rules/rules_score/test/test/fixtures/test_unit2_test.cc b/bazel/rules/rules_score/test/test/fixtures/test_unit2_test.cc deleted file mode 100644 index 5c6e3594..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/test_unit2_test.cc +++ /dev/null @@ -1,16 +0,0 @@ -// Unit tests for test_unit2 fixture -#include - -// Declarations from mock libraries -extern int mock_function_1(); -extern int mock_function_2(); - -TEST(Unit2Test, MockFunction1ReturnsExpectedValue) { - ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); - EXPECT_EQ(mock_function_1(), 42); -} - -TEST(Unit2Test, MockFunction2ReturnsExpectedValue) { - ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); - EXPECT_EQ(mock_function_2(), 84); -} diff --git a/bazel/rules/rules_score/test/test/fixtures/test_unit_test.cc b/bazel/rules/rules_score/test/test/fixtures/test_unit_test.cc deleted file mode 100644 index 2bf34a57..00000000 --- a/bazel/rules/rules_score/test/test/fixtures/test_unit_test.cc +++ /dev/null @@ -1,16 +0,0 @@ -// Unit tests for mock libraries -#include - -// Declarations from mock libraries -extern int mock_function_1(); -extern int mock_function_2(); - -TEST(MockLibTest, MockFunction1ReturnsExpectedValue) { - ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); - EXPECT_EQ(mock_function_1(), 42); -} - -TEST(MockLibTest, MockFunction2ReturnsExpectedValue) { - ::testing::Test::RecordProperty("lobster-tracing", "SeoocTest.COMP_001"); - EXPECT_EQ(mock_function_2(), 84); -} diff --git a/bazel/rules/rules_score/test/test/html_generation_test.bzl b/bazel/rules/rules_score/test/test/html_generation_test.bzl deleted file mode 100644 index 788a940f..00000000 --- a/bazel/rules/rules_score/test/test/html_generation_test.bzl +++ /dev/null @@ -1,223 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* -"""Test rules for sphinx_module HTML generation and dependencies.""" - -load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("//bazel/rules/rules_score:providers.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") - -# ============================================================================ -# Provider Tests -# ============================================================================ - -def _providers_test_impl(ctx): - """Test that sphinx_module provides the correct providers.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Verify required providers - asserts.true( - env, - SphinxModuleInfo in target_under_test, - "Target should provide SphinxModuleInfo", - ) - - asserts.true( - env, - DefaultInfo in target_under_test, - "Target should provide DefaultInfo", - ) - - return analysistest.end(env) - -providers_test = analysistest.make(_providers_test_impl) - -# ============================================================================ -# HTML Generation Tests -# ============================================================================ - -def _basic_html_generation_test_impl(ctx): - """Test that a simple document generates HTML output.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check that HTML directory exists - score_info = target_under_test[SphinxModuleInfo] - asserts.true( - env, - score_info.html_dir != None, - "Module should generate HTML directory", - ) - - return analysistest.end(env) - -basic_html_generation_test = analysistest.make(_basic_html_generation_test_impl) - -# ============================================================================ -# Needs.json Generation Tests -# ============================================================================ - -def _needs_generation_test_impl(ctx): - """Test that sphinx_module generates needs.json files.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check for SphinxNeedsInfo provider on _needs target - # Note: This test requires the _needs suffix target - asserts.true( - env, - DefaultInfo in target_under_test, - "Needs target should provide DefaultInfo", - ) - - return analysistest.end(env) - -needs_generation_test = analysistest.make(_needs_generation_test_impl) - -def _needs_transitive_test_impl(ctx): - """Test that needs.json files are collected transitively.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Verify SphinxNeedsInfo provider - asserts.true( - env, - SphinxNeedsInfo in target_under_test, - "Needs target should provide SphinxNeedsInfo", - ) - - needs_info = target_under_test[SphinxNeedsInfo] - - # Check direct needs.json file - asserts.true( - env, - needs_info.needs_json_file != None, - "Should have direct needs.json file", - ) - - # Check transitive needs collection - asserts.true( - env, - needs_info.needs_json_files != None, - "Should have transitive needs.json files depset", - ) - - return analysistest.end(env) - -needs_transitive_test = analysistest.make(_needs_transitive_test_impl) - -# ============================================================================ -# Dependency and Integration Tests -# ============================================================================ - -def _module_dependencies_test_impl(ctx): - """Test that module dependencies are properly handled.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module with dependencies should still generate HTML - asserts.true( - env, - score_info.html_dir != None, - "Module with dependencies should generate HTML", - ) - - return analysistest.end(env) - -module_dependencies_test = analysistest.make(_module_dependencies_test_impl) - -def _html_merging_test_impl(ctx): - """Test that HTML from dependencies is merged correctly.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Verify merged HTML output exists - asserts.true( - env, - score_info.html_dir != None, - "Merged HTML should be generated", - ) - - return analysistest.end(env) - -html_merging_test = analysistest.make(_html_merging_test_impl) - -# ============================================================================ -# Config Generation Tests -# ============================================================================ - -def _auto_config_generation_test_impl(ctx): - """Test that conf.py is automatically generated when not provided.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module without explicit config should still generate HTML - asserts.true( - env, - score_info.html_dir != None, - "Module with auto-generated config should produce HTML", - ) - - return analysistest.end(env) - -auto_config_generation_test = analysistest.make(_auto_config_generation_test_impl) - -def _explicit_config_test_impl(ctx): - """Test that explicit conf.py is used when provided.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module with explicit config should generate HTML - asserts.true( - env, - score_info.html_dir != None, - "Module with explicit config should produce HTML", - ) - - return analysistest.end(env) - -explicit_config_test = analysistest.make(_explicit_config_test_impl) - -# ============================================================================ -# Test Suite -# ============================================================================ - -def sphinx_module_test_suite(name): - """Create a comprehensive test suite for sphinx_module. - - Tests cover: - - Needs.json generation and transitive collection - - Module dependencies and HTML merging - - Args: - name: Name of the test suite - """ - - native.test_suite( - name = name, - tests = [ - # Needs generation - ":needs_transitive_test", - - # Dependencies and integration - ":module_dependencies_test", - ":html_merging_test", - ], - ) diff --git a/bazel/rules/rules_score/test/test/score_module_providers_test.bzl b/bazel/rules/rules_score/test/test/score_module_providers_test.bzl deleted file mode 100644 index 4dc9ead5..00000000 --- a/bazel/rules/rules_score/test/test/score_module_providers_test.bzl +++ /dev/null @@ -1,323 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* -"""Tests for sphinx_module providers and two-phase build system.""" - -load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("//bazel/rules/rules_score:providers.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") - -# ============================================================================ -# SphinxModuleInfo Provider Tests -# ============================================================================ - -def _sphinx_module_info_fields_test_impl(ctx): - """Test that SphinxModuleInfo provides all required fields.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - asserts.true( - env, - SphinxModuleInfo in target_under_test, - "Target should provide SphinxModuleInfo", - ) - - score_info = target_under_test[SphinxModuleInfo] - - # Verify html_dir field - asserts.true( - env, - hasattr(score_info, "html_dir"), - "SphinxModuleInfo should have html_dir field", - ) - - asserts.true( - env, - score_info.html_dir != None, - "html_dir should not be None", - ) - - return analysistest.end(env) - -sphinx_module_info_fields_test = analysistest.make(_sphinx_module_info_fields_test_impl) - -# ============================================================================ -# SphinxNeedsInfo Provider Tests -# ============================================================================ - -def _score_needs_info_fields_test_impl(ctx): - """Test that SphinxNeedsInfo provides all required fields.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - asserts.true( - env, - SphinxNeedsInfo in target_under_test, - "Needs target should provide SphinxNeedsInfo", - ) - - needs_info = target_under_test[SphinxNeedsInfo] - - # Verify needs_json_file field (direct file) - asserts.true( - env, - hasattr(needs_info, "needs_json_file"), - "SphinxNeedsInfo should have needs_json_file field", - ) - - asserts.true( - env, - needs_info.needs_json_file != None, - "needs_json_file should not be None", - ) - - # Verify needs_json_files field (transitive depset) - asserts.true( - env, - hasattr(needs_info, "needs_json_files"), - "SphinxNeedsInfo should have needs_json_files field", - ) - - asserts.true( - env, - needs_info.needs_json_files != None, - "needs_json_files should not be None", - ) - - # Verify it's a depset - asserts.true( - env, - type(needs_info.needs_json_files) == type(depset([])), - "needs_json_files should be a depset", - ) - - return analysistest.end(env) - -score_needs_info_fields_test = analysistest.make(_score_needs_info_fields_test_impl) - -def _score_needs_transitive_collection_test_impl(ctx): - """Test that needs.json files are collected transitively.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - needs_info = target_under_test[SphinxNeedsInfo] - - # Get the list of transitive needs files - transitive_needs = needs_info.needs_json_files.to_list() - - # Should have at least the direct needs file - asserts.true( - env, - len(transitive_needs) >= 1, - "Should have at least the direct needs.json file", - ) - - # Direct file should be in the transitive set - direct_file = needs_info.needs_json_file - asserts.true( - env, - direct_file in transitive_needs, - "Direct needs.json file should be in transitive collection", - ) - - return analysistest.end(env) - -score_needs_transitive_collection_test = analysistest.make(_score_needs_transitive_collection_test_impl) - -def _score_needs_with_deps_test_impl(ctx): - """Test that needs.json files include dependencies.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - needs_info = target_under_test[SphinxNeedsInfo] - transitive_needs = needs_info.needs_json_files.to_list() - - # Module with dependencies should have multiple needs files - # (its own + dependencies) - asserts.true( - env, - len(transitive_needs) >= 1, - "Module with dependencies should collect transitive needs.json files", - ) - - return analysistest.end(env) - -score_needs_with_deps_test = analysistest.make(_score_needs_with_deps_test_impl) - -# ============================================================================ -# Two-Phase Build Tests -# ============================================================================ - -def _two_phase_needs_first_test_impl(ctx): - """Test that Phase 1 (needs generation) works independently.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Verify SphinxNeedsInfo provider - asserts.true( - env, - SphinxNeedsInfo in target_under_test, - "Phase 1 should provide SphinxNeedsInfo", - ) - - # Verify DefaultInfo with needs.json output - asserts.true( - env, - DefaultInfo in target_under_test, - "Phase 1 should provide DefaultInfo", - ) - - default_info = target_under_test[DefaultInfo] - files = default_info.files.to_list() - - # Should have at least one file (needs.json) - asserts.true( - env, - len(files) >= 1, - "Phase 1 should output needs.json file", - ) - - return analysistest.end(env) - -two_phase_needs_first_test = analysistest.make(_two_phase_needs_first_test_impl) - -def _two_phase_html_second_test_impl(ctx): - """Test that Phase 2 (HTML generation) works with needs from Phase 1.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Verify SphinxModuleInfo provider - asserts.true( - env, - SphinxModuleInfo in target_under_test, - "Phase 2 should provide SphinxModuleInfo", - ) - - score_info = target_under_test[SphinxModuleInfo] - - # Verify HTML output - asserts.true( - env, - score_info.html_dir != None, - "Phase 2 should generate HTML directory", - ) - - return analysistest.end(env) - -two_phase_html_second_test = analysistest.make(_two_phase_html_second_test_impl) - -# ============================================================================ -# Config Generation Tests -# ============================================================================ - -def _config_auto_generation_test_impl(ctx): - """Test that conf.py is auto-generated when not provided.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module without explicit config should still build - asserts.true( - env, - score_info.html_dir != None, - "Auto-generated config should allow HTML generation", - ) - - return analysistest.end(env) - -config_auto_generation_test = analysistest.make(_config_auto_generation_test_impl) - -def _config_explicit_usage_test_impl(ctx): - """Test that explicit conf.py is used when provided.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module with explicit config should build - asserts.true( - env, - score_info.html_dir != None, - "Explicit config should allow HTML generation", - ) - - return analysistest.end(env) - -config_explicit_usage_test = analysistest.make(_config_explicit_usage_test_impl) - -# ============================================================================ -# Dependency Handling Tests -# ============================================================================ - -def _deps_html_merging_test_impl(ctx): - """Test that HTML from dependencies is merged into output.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - score_info = target_under_test[SphinxModuleInfo] - - # Module with dependencies should generate merged HTML - asserts.true( - env, - score_info.html_dir != None, - "Module with dependencies should generate merged HTML", - ) - - return analysistest.end(env) - -deps_html_merging_test = analysistest.make(_deps_html_merging_test_impl) - -def _deps_needs_collection_test_impl(ctx): - """Test that needs from dependencies are collected.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - needs_info = target_under_test[SphinxNeedsInfo] - transitive_needs = needs_info.needs_json_files.to_list() - - # Should collect needs from dependencies - asserts.true( - env, - len(transitive_needs) >= 1, - "Should collect needs.json from dependencies", - ) - - return analysistest.end(env) - -deps_needs_collection_test = analysistest.make(_deps_needs_collection_test_impl) - -# ============================================================================ -# Test Suite -# ============================================================================ - -def sphinx_module_providers_test_suite(name): - """Create a test suite for sphinx_module providers and build phases. - - Tests cover: - - Transitive needs.json collection - - Dependency handling (HTML merging, needs collection) - - Args: - name: Name of the test suite - """ - - native.test_suite( - name = name, - tests = [ - # Provider tests - ":score_needs_with_deps_test", - - # Dependency tests - ":deps_html_merging_test", - ":deps_needs_collection_test", - ], - ) diff --git a/bazel/rules/rules_score/test/test/seooc_test.bzl b/bazel/rules/rules_score/test/test/seooc_test.bzl deleted file mode 100644 index 64fd1ace..00000000 --- a/bazel/rules/rules_score/test/test/seooc_test.bzl +++ /dev/null @@ -1,135 +0,0 @@ -""" -Test suite for dependable_element macro. - -Tests the SEooC (Safety Element out of Context) functionality including: -- Index generation with artifact references -- Integration with sphinx_module -- Sphinx-needs cross-referencing -- HTML output generation -""" - -load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("//bazel/rules/rules_score:providers.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") - -def _seooc_index_generation_test_impl(ctx): - """Test that dependable_element generates proper index.rst file.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Get the generated index file - files = target_under_test[DefaultInfo].files.to_list() - - # Find index.rst in the output files - index_file = None - for f in files: - if f.basename == "index.rst": - index_file = f - break - - # Assert index file exists - asserts.true( - env, - index_file != None, - "Expected index.rst to be generated by dependable_element_index rule", - ) - - return analysistest.end(env) - -seooc_index_generation_test = analysistest.make( - impl = _seooc_index_generation_test_impl, -) - -def _seooc_artifacts_copied_test_impl(ctx): - """Test that all dependable element artifacts are copied to output directory.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - files = target_under_test[DefaultInfo].files.to_list() - - # Expected artifact basenames - these come from the SphinxSourcesInfo providers - # and are filtered to only include .rst/.md files for the index - expected_artifacts = [ - "feat_req_feat_req_trlc.rst", # from requirements (feature_requirements rendered from TRLC) - "dfa.rst", # from :dependability_analysis_target - ] - - # Check each artifact exists - actual_basenames = [f.basename for f in files] - for artifact in expected_artifacts: - asserts.true( - env, - artifact in actual_basenames, - "Expected artifact '{}' to be in output files".format(artifact), - ) - - return analysistest.end(env) - -seooc_artifacts_copied_test = analysistest.make( - impl = _seooc_artifacts_copied_test_impl, -) - -def _seooc_sphinx_module_generated_test_impl(ctx): - """Test that dependable_element generates sphinx_module with HTML output.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check that SphinxModuleInfo provider exists - asserts.true( - env, - SphinxModuleInfo in target_under_test, - "Expected dependable_element to provide SphinxModuleInfo from sphinx_module", - ) - - return analysistest.end(env) - -seooc_sphinx_module_generated_test = analysistest.make( - impl = _seooc_sphinx_module_generated_test_impl, -) - -def _seooc_needs_provider_test_impl(ctx): - """Test that dependable_element generates needs provider for cross-referencing.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check that SphinxNeedsInfo provider exists - asserts.true( - env, - SphinxNeedsInfo in target_under_test, - "Expected dependable_element_needs to provide SphinxNeedsInfo", - ) - - return analysistest.end(env) - -seooc_needs_provider_test = analysistest.make( - impl = _seooc_needs_provider_test_impl, -) - -def _seooc_description_test_impl(ctx): - """Test that SEooC includes description in generated index.rst.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Get the generated index file - files = target_under_test[DefaultInfo].files.to_list() - - # Find index.rst - index_file = None - for f in files: - if f.basename == "index.rst": - index_file = f - break - - # Note: We can't easily read file contents in analysis test, - # but we can verify the file exists. The description content - # would be validated through integration tests or manual inspection. - asserts.true( - env, - index_file != None, - "Expected index.rst to exist for description validation", - ) - - return analysistest.end(env) - -seooc_description_test = analysistest.make( - impl = _seooc_description_test_impl, -) diff --git a/bazel/rules/rules_score/test/test/test_safety_analysis_tools.py b/bazel/rules/rules_score/test/test/test_safety_analysis_tools.py deleted file mode 100644 index 302e7835..00000000 --- a/bazel/rules/rules_score/test/test/test_safety_analysis_tools.py +++ /dev/null @@ -1,359 +0,0 @@ -# ******************************************************************************* -# 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 -# ******************************************************************************* - -"""Tests for safety_analysis_tools.""" - -import json -import logging -import os -import sys -import tempfile -import unittest - -from safety_analysis_tools import ( - LOBSTER_GENERATOR, - LOBSTER_SCHEMA, - LOBSTER_VERSION, - _is_valid_trlc_fqn, - _parse_quoted_args, - create_lobster_output, - extract_fta_items, - main, - preprocess_puml, -) - -# --------------------------------------------------------------------------- -# Fixtures -# --------------------------------------------------------------------------- - -SAMPLE_FTA = """\ -@startuml - -!include fta_metamodel.puml - -$TopEvent("Failure takes over", "Pkg.TopFailure") -$OrGate("OG1", "Pkg.TopFailure") -$BasicEvent("Bad luck", "Pkg.BadLuck", "OG1") -$IntermediateEvent("Angry", "IEF", "OG1") -$AndGate("AG2", "IEF") -$BasicEvent("No Cookies", "Pkg.NoCookies", "AG2") -$BasicEvent("No Coffee", "Pkg.NoCoffee", "AG2") - -@enduml -""" - -EMPTY_FTA = """\ -@startuml -' No events here -@enduml -""" - -SAMPLE_METAMODEL = """\ -@startuml - -' AND gate sprite -sprite $and placeholder - -!procedure $TopEvent($name, $alias) - rectangle "$name" as $alias -!endprocedure - -!procedure $BasicEvent($name, $alias, $connection) - "$name" as $alias - $alias -u-> $connection -!endprocedure - -@enduml -""" - - -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- - - -def _write_file(content: str, directory: str, name: str) -> str: - path = os.path.join(directory, name) - with open(path, "w", encoding="utf-8") as fh: - fh.write(content) - return path - - -# --------------------------------------------------------------------------- -# _parse_quoted_args -# --------------------------------------------------------------------------- - - -class TestParseQuotedArgs(unittest.TestCase): - def test_returns_none_when_proc_absent(self): - self.assertIsNone(_parse_quoted_args("$OrGate()", "$TopEvent")) - - def test_extracts_two_args(self): - self.assertEqual( - _parse_quoted_args('$TopEvent("My name", "Pkg.Record")', "$TopEvent"), - ["My name", "Pkg.Record"], - ) - - def test_extracts_three_args(self): - self.assertEqual( - _parse_quoted_args( - '$BasicEvent("Bad luck", "Pkg.BadLuck", "OG1")', "$BasicEvent" - ), - ["Bad luck", "Pkg.BadLuck", "OG1"], - ) - - def test_returns_none_on_missing_open_paren(self): - self.assertIsNone(_parse_quoted_args("$TopEvent", "$TopEvent")) - - def test_returns_none_on_no_quoted_args(self): - self.assertIsNone(_parse_quoted_args("$TopEvent()", "$TopEvent")) - - def test_ignores_content_after_close_paren(self): - line = '$TopEvent("a", "b") $BasicEvent("c", "d", "e")' - self.assertEqual(_parse_quoted_args(line, "$TopEvent"), ["a", "b"]) - - -# --------------------------------------------------------------------------- -# _is_valid_trlc_fqn -# --------------------------------------------------------------------------- - - -class TestIsValidTrlcFqn(unittest.TestCase): - def test_valid(self): - self.assertTrue(_is_valid_trlc_fqn("Pkg.Record")) - self.assertTrue(_is_valid_trlc_fqn("SampleLib.FailureMode")) - - def test_no_dot(self): - self.assertFalse(_is_valid_trlc_fqn("NoDotsHere")) - - def test_too_many_dots(self): - self.assertFalse(_is_valid_trlc_fqn("A.B.C")) - - def test_empty_string(self): - self.assertFalse(_is_valid_trlc_fqn("")) - - def test_dot_only(self): - self.assertFalse(_is_valid_trlc_fqn(".")) - - -# --------------------------------------------------------------------------- -# extract_fta_items -# --------------------------------------------------------------------------- - - -class TestExtractFtaItems(unittest.TestCase): - def test_extracts_top_event(self): - with tempfile.TemporaryDirectory() as tmp: - path = _write_file(SAMPLE_FTA, tmp, "fta.puml") - items = extract_fta_items(path) - - top_events = [i for i in items if i["kind"] == "TopEvent"] - self.assertEqual(len(top_events), 1) - self.assertEqual(top_events[0]["name"], "Pkg.TopFailure") - self.assertEqual(top_events[0]["tag"], "fta Pkg.TopFailure") - self.assertIn("req Pkg.TopFailure", top_events[0]["refs"]) - - def test_extracts_basic_events(self): - with tempfile.TemporaryDirectory() as tmp: - path = _write_file(SAMPLE_FTA, tmp, "fta.puml") - items = extract_fta_items(path) - - basic_aliases = {i["name"] for i in items if i["kind"] == "BasicEvent"} - self.assertEqual( - basic_aliases, {"Pkg.BadLuck", "Pkg.NoCookies", "Pkg.NoCoffee"} - ) - - def test_ignores_gates_and_intermediate_events(self): - with tempfile.TemporaryDirectory() as tmp: - path = _write_file(SAMPLE_FTA, tmp, "fta.puml") - items = extract_fta_items(path) - self.assertEqual({i["kind"] for i in items}, {"TopEvent", "BasicEvent"}) - - def test_refs_point_to_alias(self): - with tempfile.TemporaryDirectory() as tmp: - path = _write_file(SAMPLE_FTA, tmp, "fta.puml") - for item in extract_fta_items(path): - self.assertIn(f"req {item['name']}", item["refs"]) - - def test_location_contains_file_and_line(self): - with tempfile.TemporaryDirectory() as tmp: - path = _write_file(SAMPLE_FTA, tmp, "fta.puml") - for item in extract_fta_items(path): - self.assertEqual(item["location"]["kind"], "file") - self.assertEqual(item["location"]["file"], path) - self.assertGreater(item["location"]["line"], 0) - - def test_one_match_per_line(self): - content = '$TopEvent("name", "A.B") $BasicEvent("x", "C.D", "conn")\n' - with tempfile.TemporaryDirectory() as tmp: - path = _write_file(content, tmp, "fta.puml") - items = extract_fta_items(path) - self.assertEqual(len(items), 1) - self.assertEqual(items[0]["kind"], "TopEvent") - - def test_empty_diagram_returns_empty_list(self): - with tempfile.TemporaryDirectory() as tmp: - path = _write_file(EMPTY_FTA, tmp, "fta.puml") - self.assertEqual(extract_fta_items(path), []) - - def test_missing_file_raises(self): - with tempfile.TemporaryDirectory() as tmp: - with self.assertRaises(OSError): - extract_fta_items(os.path.join(tmp, "nonexistent.puml")) - - def test_invalid_alias_logs_warning(self): - content = '$TopEvent("bad alias", "NoDotsHere")\n' - with tempfile.TemporaryDirectory() as tmp: - path = _write_file(content, tmp, "fta.puml") - with self.assertLogs(level="WARNING") as log: - extract_fta_items(path) - self.assertTrue(any("does not look like a valid" in m for m in log.output)) - - def test_valid_alias_produces_no_fqn_warning(self): - with tempfile.TemporaryDirectory() as tmp: - path = _write_file(SAMPLE_FTA, tmp, "fta.puml") - with self.assertLogs(level="WARNING") as log: - logging.getLogger().warning( - "sentinel" - ) # ensure assertLogs doesn't fail - extract_fta_items(path) - self.assertFalse(any("does not look like a valid" in m for m in log.output)) - - -# --------------------------------------------------------------------------- -# preprocess_puml -# --------------------------------------------------------------------------- - - -class TestPreprocessPuml(unittest.TestCase): - def _write_metamodel(self, directory: str) -> str: - return _write_file(SAMPLE_METAMODEL, directory, "fta_metamodel.puml") - - def test_inlines_metamodel_content(self): - with tempfile.TemporaryDirectory() as tmp: - meta = self._write_metamodel(tmp) - src = _write_file( - '@startuml\n!include fta_metamodel.puml\n$TopEvent("x", "A.B")\n@enduml\n', - tmp, - "fta.puml", - ) - out = os.path.join(tmp, "out.puml") - preprocess_puml(src, meta, out) - - content = open(out).read() - self.assertNotIn("!include fta_metamodel.puml", content) - self.assertIn("!procedure $TopEvent", content) - self.assertIn("sprite $and", content) - self.assertIn('$TopEvent("x", "A.B")', content) - - def test_metamodel_markers_are_stripped(self): - with tempfile.TemporaryDirectory() as tmp: - meta = self._write_metamodel(tmp) - src = _write_file( - "@startuml\n!include fta_metamodel.puml\n@enduml\n", - tmp, - "fta.puml", - ) - out = os.path.join(tmp, "out.puml") - preprocess_puml(src, meta, out) - self.assertNotIn("@startuml\n@startuml", open(out).read()) - - def test_diagram_without_include_is_unchanged(self): - original = '@startuml\n$TopEvent("x", "A.B")\n@enduml\n' - with tempfile.TemporaryDirectory() as tmp: - meta = self._write_metamodel(tmp) - src = _write_file(original, tmp, "fta.puml") - out = os.path.join(tmp, "out.puml") - preprocess_puml(src, meta, out) - self.assertEqual(open(out).read(), original) - - -# --------------------------------------------------------------------------- -# create_lobster_output -# --------------------------------------------------------------------------- - - -class TestCreateLobsterOutput(unittest.TestCase): - def test_envelope_fields(self): - items = [{"tag": "fta A.B", "name": "A.B"}] - output = create_lobster_output(items) - self.assertEqual(output["generator"], LOBSTER_GENERATOR) - self.assertEqual(output["schema"], LOBSTER_SCHEMA) - self.assertEqual(output["version"], LOBSTER_VERSION) - self.assertEqual(output["data"], items) - - def test_empty_items(self): - self.assertEqual(create_lobster_output([])["data"], []) - - -# --------------------------------------------------------------------------- -# main (CLI integration) -# --------------------------------------------------------------------------- - - -class TestMain(unittest.TestCase): - """Integration tests for the flat CLI: --metamodel --output-dir --lobster inputs...""" - - def _run_main(self, tmp: str, *puml_paths: str) -> dict: - """Invoke main() and return the parsed lobster JSON dict.""" - meta = _write_file(SAMPLE_METAMODEL, tmp, "fta_metamodel.puml") - out_dir = os.path.join(tmp, "preprocessed") - lobster_path = os.path.join(tmp, "out.lobster") - - saved = sys.argv - try: - sys.argv = [ - "safety_analysis_tools", - "--metamodel", - meta, - "--output-dir", - out_dir, - "--lobster", - lobster_path, - *puml_paths, - ] - main() - finally: - sys.argv = saved - - with open(lobster_path, encoding="utf-8") as fh: - return json.load(fh) - - def test_produces_valid_lobster_file(self): - with tempfile.TemporaryDirectory() as tmp: - puml = _write_file(SAMPLE_FTA, tmp, "fta.puml") - data = self._run_main(tmp, puml) - self.assertEqual(data["schema"], LOBSTER_SCHEMA) - self.assertEqual(len(data["data"]), 4) # 1 TopEvent + 3 BasicEvents - - def test_produces_preprocessed_puml_files(self): - with tempfile.TemporaryDirectory() as tmp: - puml = _write_file(SAMPLE_FTA, tmp, "fta.puml") - self._run_main(tmp, puml) - - preprocessed = os.path.join(tmp, "preprocessed", "fta.puml") - self.assertTrue(os.path.exists(preprocessed)) - content = open(preprocessed).read() - self.assertNotIn("!include fta_metamodel.puml", content) - self.assertIn("!procedure $TopEvent", content) - - def test_multiple_inputs_aggregate_lobster_items(self): - with tempfile.TemporaryDirectory() as tmp: - p1 = _write_file(SAMPLE_FTA, tmp, "a.puml") - p2 = _write_file(SAMPLE_FTA, tmp, "b.puml") - data = self._run_main(tmp, p1, p2) - self.assertEqual(len(data["data"]), 8) # 4 items × 2 files - - -if __name__ == "__main__": - unittest.main() diff --git a/bazel/rules/rules_score/test/test/unit_component_test.bzl b/bazel/rules/rules_score/test/test/unit_component_test.bzl deleted file mode 100644 index dc607af9..00000000 --- a/bazel/rules/rules_score/test/test/unit_component_test.bzl +++ /dev/null @@ -1,181 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* -""" -Test suite for unit, component, and dependable_element rules. - -Tests the new hierarchical structure for S-CORE process compliance: -- Unit: smallest testable element -- Component: collection of units -- Dependable Element: complete SEooC with full documentation -""" - -load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("//bazel/rules/rules_score:providers.bzl", "ComponentInfo", "SphinxSourcesInfo", "UnitInfo") - -# ============================================================================ -# Unit Tests -# ============================================================================ - -def _unit_provider_test_impl(ctx): - """Test that unit rule provides UnitInfo.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check UnitInfo provider exists - asserts.true( - env, - UnitInfo in target_under_test, - "Unit should provide UnitInfo", - ) - - unit_info = target_under_test[UnitInfo] - - # Verify fields are populated - asserts.true( - env, - unit_info.name != None, - "UnitInfo should have name field", - ) - - asserts.true( - env, - unit_info.unit_design != None, - "UnitInfo should have unit_design field", - ) - - asserts.true( - env, - unit_info.implementation != None, - "UnitInfo should have implementation field", - ) - - asserts.true( - env, - unit_info.tests != None, - "UnitInfo should have tests field", - ) - - return analysistest.end(env) - -unit_provider_test = analysistest.make(_unit_provider_test_impl) - -def _unit_sphinx_sources_test_impl(ctx): - """Test that unit rule provides SphinxSourcesInfo.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check SphinxSourcesInfo provider exists - asserts.true( - env, - SphinxSourcesInfo in target_under_test, - "Unit should provide SphinxSourcesInfo", - ) - - return analysistest.end(env) - -unit_sphinx_sources_test = analysistest.make(_unit_sphinx_sources_test_impl) - -# ============================================================================ -# Component Tests -# ============================================================================ - -def _component_provider_test_impl(ctx): - """Test that component rule provides ComponentInfo.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check ComponentInfo provider exists - asserts.true( - env, - ComponentInfo in target_under_test, - "Component should provide ComponentInfo", - ) - - comp_info = target_under_test[ComponentInfo] - - # Verify fields are populated - asserts.true( - env, - comp_info.name != None, - "ComponentInfo should have name field", - ) - - asserts.true( - env, - comp_info.requirements != None, - "ComponentInfo should have component_requirements field", - ) - - asserts.true( - env, - comp_info.components != None, - "ComponentInfo should have components field", - ) - - asserts.true( - env, - comp_info.tests != None, - "ComponentInfo should have tests field", - ) - - asserts.true( - env, - comp_info.architecture != None, - "ComponentInfo should have architecture field", - ) - - return analysistest.end(env) - -component_provider_test = analysistest.make(_component_provider_test_impl) - -def _component_sphinx_sources_test_impl(ctx): - """Test that component rule provides SphinxSourcesInfo.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check SphinxSourcesInfo provider exists - asserts.true( - env, - SphinxSourcesInfo in target_under_test, - "Component should provide SphinxSourcesInfo", - ) - - return analysistest.end(env) - -component_sphinx_sources_test = analysistest.make(_component_sphinx_sources_test_impl) - -# ============================================================================ -# Dependable Element Tests -# ============================================================================ -# Note: Provider tests removed as dependable_element no longer creates a -# separate provider target. The main target is now a sphinx_module. - -# ============================================================================ -# Test Suite Definition -# ============================================================================ - -def unit_component_test_suite(name): - """Create test suite for unit, component, and dependable_element rules. - - Args: - name: Name of the test suite - """ - native.test_suite( - name = name, - tests = [ - ":unit_provider_test", - ":unit_sphinx_sources_test", - ":component_provider_test", - ":component_sphinx_sources_test", - ], - ) diff --git a/bazel/rules/rules_score/test/unit_component_test.bzl b/bazel/rules/rules_score/test/unit_component_test.bzl deleted file mode 100644 index 7d7b9181..00000000 --- a/bazel/rules/rules_score/test/unit_component_test.bzl +++ /dev/null @@ -1,175 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 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 -# ******************************************************************************* -""" -Test suite for unit, component, and dependable_element rules. - -Tests the new hierarchical structure for S-CORE process compliance: -- Unit: smallest testable element -- Component: collection of units -- Dependable Element: complete SEooC with full documentation -""" - -load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("@score_tooling//bazel/rules/rules_score:providers.bzl", "ComponentInfo", "SphinxSourcesInfo", "UnitInfo") - -# ============================================================================ -# Unit Tests -# ============================================================================ - -def _unit_provider_test_impl(ctx): - """Test that unit rule provides UnitInfo.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check UnitInfo provider exists - asserts.true( - env, - UnitInfo in target_under_test, - "Unit should provide UnitInfo", - ) - - unit_info = target_under_test[UnitInfo] - - # Verify fields are populated - asserts.true( - env, - unit_info.name != None, - "UnitInfo should have name field", - ) - - asserts.true( - env, - unit_info.unit_design != None, - "UnitInfo should have unit_design field", - ) - - asserts.true( - env, - unit_info.implementation != None, - "UnitInfo should have implementation field", - ) - - asserts.true( - env, - unit_info.tests != None, - "UnitInfo should have tests field", - ) - - return analysistest.end(env) - -unit_provider_test = analysistest.make(_unit_provider_test_impl) - -def _unit_sphinx_sources_test_impl(ctx): - """Test that unit rule provides SphinxSourcesInfo.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check SphinxSourcesInfo provider exists - asserts.true( - env, - SphinxSourcesInfo in target_under_test, - "Unit should provide SphinxSourcesInfo", - ) - - return analysistest.end(env) - -unit_sphinx_sources_test = analysistest.make(_unit_sphinx_sources_test_impl) - -# ============================================================================ -# Component Tests -# ============================================================================ - -def _component_provider_test_impl(ctx): - """Test that component rule provides ComponentInfo.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check ComponentInfo provider exists - asserts.true( - env, - ComponentInfo in target_under_test, - "Component should provide ComponentInfo", - ) - - comp_info = target_under_test[ComponentInfo] - - # Verify fields are populated - asserts.true( - env, - comp_info.name != None, - "ComponentInfo should have name field", - ) - - asserts.true( - env, - comp_info.requirements != None, - "ComponentInfo should have component_requirements field", - ) - - asserts.true( - env, - comp_info.components != None, - "ComponentInfo should have components field", - ) - - asserts.true( - env, - comp_info.tests != None, - "ComponentInfo should have tests field", - ) - - return analysistest.end(env) - -component_provider_test = analysistest.make(_component_provider_test_impl) - -def _component_sphinx_sources_test_impl(ctx): - """Test that component rule provides SphinxSourcesInfo.""" - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - - # Check SphinxSourcesInfo provider exists - asserts.true( - env, - SphinxSourcesInfo in target_under_test, - "Component should provide SphinxSourcesInfo", - ) - - return analysistest.end(env) - -component_sphinx_sources_test = analysistest.make(_component_sphinx_sources_test_impl) - -# ============================================================================ -# Dependable Element Tests -# ============================================================================ -# Note: Provider tests removed as dependable_element no longer creates a -# separate provider target. The main target is now a sphinx_module. - -# ============================================================================ -# Test Suite Definition -# ============================================================================ - -def unit_component_test_suite(name): - """Create test suite for unit, component, and dependable_element rules. - - Args: - name: Name of the test suite - """ - native.test_suite( - name = name, - tests = [ - ":unit_provider_test", - ":unit_sphinx_sources_test", - ":component_provider_test", - ":component_sphinx_sources_test", - ], - ) From 4477dbd3d5976a3f4a812a2c2708f681d3bbc58c Mon Sep 17 00:00:00 2001 From: Jochen Hoenle <173445474+hoe-jo@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:52:33 +0200 Subject: [PATCH 05/10] fix safety analysis --- .../examples/seooc/safety_analysis/BUILD | 4 +++- .../safety_analysis/assets/fta_example.puml | 18 ++++++++++++++++++ .../safety_analysis/assets/fta_example.svg | 1 + .../seooc/safety_analysis/safety_analysis.md | 2 ++ .../seooc/safety_analysis/sample_fta.svg | 1 + plantuml/fta_metamodel.puml | 4 ---- 6 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/assets/fta_example.puml create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/assets/fta_example.svg create mode 100644 bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.svg diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD b/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD index 8ccecab3..0282b0af 100644 --- a/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD @@ -37,13 +37,15 @@ trlc_requirements( spec = [ "@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model", ], + deps = [ + ":sample_fmea_failure_modes", + ], visibility = ["//visibility:public"], ) trlc_requirements_test( name = "sample_fmea_test", reqs = [ - ":sample_fmea_failure_modes", ":sample_fmea_control_measures", ], visibility = ["//visibility:public"], diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/fta_example.puml b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/fta_example.puml new file mode 100644 index 00000000..be185b82 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/fta_example.puml @@ -0,0 +1,18 @@ +@startuml + +!include ../../../../../../../plantuml/fta_metamodel.puml + +' Top level (skeleton) +$TopEvent("SampleFailureMode takes over the world", "SampleLibrary.SampleFailureMode") + +' 2nd level gates and events +$OrGate("OG1", "SampleLibrary.SampleFailureMode") + +$IntermediateEvent("SampleFailureMode is Angry", "IEF", "OG1") +$BasicEvent("Just bad luck", "SampleLibrary.JustBadLuck", "OG1") + +' 3rd level cascades from AGF +$AndGate("AG2", "IEF") +$BasicEvent("No More Cookies", "SampleLibrary.NoMoreCookies", "AG2") +$BasicEvent("No More Coffee", "SampleLibrary.NoMoreCoffee", "AG2") +@enduml diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/fta_example.svg b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/fta_example.svg new file mode 100644 index 00000000..04867658 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/assets/fta_example.svg @@ -0,0 +1 @@ +SampleFailureMode takes over the worldSampleFailureMode is AngryJust bad luckNo More CookiesNo More Coffee diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/safety_analysis.md b/bazel/rules/rules_score/examples/seooc/safety_analysis/safety_analysis.md index 7a2f9416..63686cd0 100644 --- a/bazel/rules/rules_score/examples/seooc/safety_analysis/safety_analysis.md +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/safety_analysis.md @@ -89,6 +89,8 @@ $BasicEvent("No More Coffee", "SampleLibrary.NoMoreCoffee", "AG2") ``` +![FTA Example](assets/fta_example.svg) + ## Control Measures For each BasicEvent a Control Measure shall be derived. This will be performed again in TRLC. The Mapping between PlantUML and TRLC will be done again based on the ID. So for our case we will define a Control Measure for NoMoreCookies: diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.svg b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.svg new file mode 100644 index 00000000..a90519a7 --- /dev/null +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.svg @@ -0,0 +1 @@ +Welcome to PlantUML! You can start with a simple UML Diagram like: Bob->Alice: Hello Or class Example You will find more information about PlantUML syntax onhttps://plantuml.com (Details by typinglicensekeyword) PlantUML 1.2026.1[From sample_fta.puml (line 16) ] @startuml !include fta_metamodel.pumlcannot include fta_metamodel.puml diff --git a/plantuml/fta_metamodel.puml b/plantuml/fta_metamodel.puml index 09aabc5a..770f7a54 100644 --- a/plantuml/fta_metamodel.puml +++ b/plantuml/fta_metamodel.puml @@ -11,8 +11,6 @@ ' SPDX-License-Identifier: Apache-2.0 ' ******************************************************************************* -@startuml - ' AND gate: flat bottom + elliptic arch on top sprite $and @@ -59,5 +57,3 @@ sprite $transferin > as $alias $alias -u-> $connection !endprocedure - -@enduml From 912572576a9754f2f8d5a8fa21017f12f901279f Mon Sep 17 00:00:00 2001 From: Ulrich Huber Date: Thu, 16 Apr 2026 16:32:31 +0200 Subject: [PATCH 06/10] Fix formatting --- MODULE.bazel | 1 + bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD | 2 +- tools/sphinx/BUILD | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index be9a983f..873df5df 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -224,6 +224,7 @@ bazel_dep(name = "googletest", version = "1.17.0.bcr.2", dev_dependency = True) # Blueprint Maven ############################################################################### bazel_dep(name = "rules_jvm_external", version = "6.10") + maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") maven.install( name = "blueprint_maven_dependencies", diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD b/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD index 0282b0af..1a4847af 100644 --- a/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/BUILD @@ -37,10 +37,10 @@ trlc_requirements( spec = [ "@score_tooling//bazel/rules/rules_score/trlc/config:score_requirements_model", ], + visibility = ["//visibility:public"], deps = [ ":sample_fmea_failure_modes", ], - visibility = ["//visibility:public"], ) trlc_requirements_test( diff --git a/tools/sphinx/BUILD b/tools/sphinx/BUILD index 6abb006d..e0168a64 100644 --- a/tools/sphinx/BUILD +++ b/tools/sphinx/BUILD @@ -12,9 +12,9 @@ # ******************************************************************************* load("@pip_rules_score//:requirements.bzl", "requirement") +load("@pip_tooling//:requirements.bzl", "requirement") load("@rules_java//java:defs.bzl", "java_binary") load("@rules_python//sphinxdocs:sphinx.bzl", "sphinx_build_binary") -load("@pip_tooling//:requirements.bzl", "requirement") java_binary( name = "plantuml", From de857ace571ffb0bf1e79dd8cb1dc112768f0337 Mon Sep 17 00:00:00 2001 From: Ulrich Huber Date: Thu, 16 Apr 2026 16:33:07 +0200 Subject: [PATCH 07/10] Adjust test target in CI workflow --- .github/workflows/tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c370f5f2..f8f4e440 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,5 +37,4 @@ jobs: bazel test //coverage/tests:all - name: Run rules_score tests run: | - cd bazel/rules/rules_score/test - bazel test //... + bazel test //bazel/rules/rules_score/... From 6b00d998dfbb41765ff4cacbccc29a640135708e Mon Sep 17 00:00:00 2001 From: Ulrich Huber Date: Thu, 16 Apr 2026 16:35:03 +0200 Subject: [PATCH 08/10] Drop unused lobster repo rules --- third_party/lobster/BUILD | 14 ------------- third_party/lobster/lobster.bzl | 37 --------------------------------- 2 files changed, 51 deletions(-) delete mode 100644 third_party/lobster/BUILD delete mode 100644 third_party/lobster/lobster.bzl diff --git a/third_party/lobster/BUILD b/third_party/lobster/BUILD deleted file mode 100644 index 0381c757..00000000 --- a/third_party/lobster/BUILD +++ /dev/null @@ -1,14 +0,0 @@ -# ******************************************************************************* -# 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 -# ******************************************************************************* - -exports_files(["lobster.bzl"]) diff --git a/third_party/lobster/lobster.bzl b/third_party/lobster/lobster.bzl deleted file mode 100644 index c8d03a24..00000000 --- a/third_party/lobster/lobster.bzl +++ /dev/null @@ -1,37 +0,0 @@ -# ******************************************************************************* -# 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 -# ******************************************************************************* - -def github_urls(path): - return [ - "https://github.com/" + path, - ] - -def _lobster_repository_impl(repository_ctx): - _VERSION = "0.14.1" - _PATH = "bmw-software-engineering/lobster/archive/refs/tags/lobster-{version}.tar.gz".format(version = _VERSION) - repository_ctx.download_and_extract( - url = github_urls(_PATH), - sha256 = "5a0b86c62cadc872dcb1b79485ba72953400bcdc42f97c5b5aefe210e92ce6ff", - stripPrefix = "lobster-lobster-{version}".format(version = _VERSION), - ) - -lobster_repository = repository_rule( - implementation = _lobster_repository_impl, -) - -def _lobster_impl(ctx): - lobster_repository(name = "lobster") - -lobster_ext = module_extension( - implementation = _lobster_impl, -) From 5c8dbee660a7f852c06ec1dd8c6e00a50949b00d Mon Sep 17 00:00:00 2001 From: Jochen Hoenle <173445474+hoe-jo@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:20:55 +0200 Subject: [PATCH 09/10] fix tracing bugs --- .../rules_score/config/lobster_de.conf.tpl | 1 - .../rules_score/config/lobster_feat_req.yaml | 1 + .../examples/seooc/design/public_api.puml | 2 +- .../sample_fmea_failure_modes.trlc | 9 -------- .../seooc/safety_analysis/sample_fta.puml | 1 + .../examples/seooc/unit_1/foo_test.cpp | 23 +++++++++++++++++++ 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/bazel/rules/rules_score/config/lobster_de.conf.tpl b/bazel/rules/rules_score/config/lobster_de.conf.tpl index 74c4aa73..6f128f65 100644 --- a/bazel/rules/rules_score/config/lobster_de.conf.tpl +++ b/bazel/rules/rules_score/config/lobster_de.conf.tpl @@ -19,7 +19,6 @@ implementation "Architecture" { implementation "Public API" { {PUBLIC_API_SOURCES} - trace to: "Component Requirements"; } requirements "Failure Modes" { diff --git a/bazel/rules/rules_score/config/lobster_feat_req.yaml b/bazel/rules/rules_score/config/lobster_feat_req.yaml index 42a17596..a78977fc 100644 --- a/bazel/rules/rules_score/config/lobster_feat_req.yaml +++ b/bazel/rules/rules_score/config/lobster_feat_req.yaml @@ -22,4 +22,5 @@ conversion-rules: - package: ScoreReq record-type: FeatReq namespace: req + version-field: version description-fields: description diff --git a/bazel/rules/rules_score/examples/seooc/design/public_api.puml b/bazel/rules/rules_score/examples/seooc/design/public_api.puml index 65810f92..e4bdcb66 100644 --- a/bazel/rules/rules_score/examples/seooc/design/public_api.puml +++ b/bazel/rules/rules_score/examples/seooc/design/public_api.puml @@ -15,7 +15,7 @@ package "SampleLibraryAPI" as SampleLibraryAPI { interface "GetNumber" as GetNumber - interface "SetNumber" as SetNumber + ' interface "SetNumber" as SetNumber } @enduml diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_failure_modes.trlc b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_failure_modes.trlc index 73ccd8fa..745e0860 100644 --- a/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_failure_modes.trlc +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fmea_failure_modes.trlc @@ -22,12 +22,3 @@ ScoreReq.FailureMode SampleFailureMode{ safety = ScoreReq.Asil.B interface = "SampleLibraryAPI.GetNumber" } - -ScoreReq.FailureMode SampleFailureModeSetNumber{ - guideword = ScoreReq.GuideWord.LossOfFunction - description = "SampleFailureModeSetNumber takes over the world" - failureeffect = "The world as we know it will end" - version = 1 - safety = ScoreReq.Asil.B - interface = "SampleLibraryAPI.SetNumber" -} diff --git a/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.puml b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.puml index ea589d16..cfc194ce 100644 --- a/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.puml +++ b/bazel/rules/rules_score/examples/seooc/safety_analysis/sample_fta.puml @@ -28,4 +28,5 @@ $BasicEvent("Just bad luck", "SampleLibrary.JustBadLuck", "OG1") $AndGate("AG2", "IEF") $BasicEvent("No More Cookies", "SampleLibrary.NoMoreCookies", "AG2") $BasicEvent("No More Coffee", "SampleLibrary.NoMoreCoffee", "AG2") + @enduml diff --git a/bazel/rules/rules_score/examples/seooc/unit_1/foo_test.cpp b/bazel/rules/rules_score/examples/seooc/unit_1/foo_test.cpp index 19d98244..42e9c575 100644 --- a/bazel/rules/rules_score/examples/seooc/unit_1/foo_test.cpp +++ b/bazel/rules/rules_score/examples/seooc/unit_1/foo_test.cpp @@ -23,3 +23,26 @@ TEST(Foo, GetNumber) { EXPECT_EQ(unit.GetNumber(), 42u); } + +TEST(Foo, IsFinal) { + ::testing::Test::RecordProperty("lobster-tracing", + "SampleComponent.REQ_COMP_002"); + // Foo is declared final; extensibility is enforced at compile time. + SUCCEED(); +} + +TEST(Foo, InitializesToKnownValue) { + ::testing::Test::RecordProperty("lobster-tracing", + "SampleComponentExtra.REQ_COMP_EXTRA_001"); + + unit_1::Foo unit{}; + EXPECT_EQ(unit.GetNumber(), 42u); +} + +TEST(Foo, ValueConsistentAcrossReads) { + ::testing::Test::RecordProperty("lobster-tracing", + "SampleComponentExtra.REQ_COMP_EXTRA_002"); + + const unit_1::Foo unit{}; + EXPECT_EQ(unit.GetNumber(), unit.GetNumber()); +} From 70ccbbaa5489a2ee72e431b6fd1a4b46a9348ac2 Mon Sep 17 00:00:00 2001 From: Ulrich Huber Date: Mon, 20 Apr 2026 13:45:07 +0200 Subject: [PATCH 10/10] Bump tests to new trlc and lobster dependencies --- cr_checker/tests/MODULE.bazel | 4 ++-- python_basics/integration_tests/MODULE.bazel | 4 ++-- starpls/integration_tests/MODULE.bazel | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cr_checker/tests/MODULE.bazel b/cr_checker/tests/MODULE.bazel index 2d157c5a..728ebadd 100644 --- a/cr_checker/tests/MODULE.bazel +++ b/cr_checker/tests/MODULE.bazel @@ -49,14 +49,14 @@ local_path_override( bazel_dep(name = "trlc", version = "0.0.0") git_override( module_name = "trlc", - commit = "c7750ebd0520996e05b9a9bc91eb6a4f62030f22", + commit = "1872957ae981ae632a0ec6eafb5b8d3aa44bdb86", remote = "https://github.com/bmw-software-engineering/trlc.git", ) bazel_dep(name = "lobster", version = "0.0.0") git_override( module_name = "lobster", - commit = "56881461f9d3fde2918d1731aa5937aaf64cd67c", + commit = "94ed5961ca28ee1b840cd8a938138c17ae4da671", remote = "https://github.com/bmw-software-engineering/lobster.git", ) diff --git a/python_basics/integration_tests/MODULE.bazel b/python_basics/integration_tests/MODULE.bazel index 28f0cef2..d9f3340f 100644 --- a/python_basics/integration_tests/MODULE.bazel +++ b/python_basics/integration_tests/MODULE.bazel @@ -61,13 +61,13 @@ local_path_override( bazel_dep(name = "trlc", version = "0.0.0") git_override( module_name = "trlc", - commit = "c7750ebd0520996e05b9a9bc91eb6a4f62030f22", + commit = "1872957ae981ae632a0ec6eafb5b8d3aa44bdb86", remote = "https://github.com/bmw-software-engineering/trlc.git", ) bazel_dep(name = "lobster", version = "0.0.0") git_override( module_name = "lobster", - commit = "56881461f9d3fde2918d1731aa5937aaf64cd67c", + commit = "94ed5961ca28ee1b840cd8a938138c17ae4da671", remote = "https://github.com/bmw-software-engineering/lobster.git", ) diff --git a/starpls/integration_tests/MODULE.bazel b/starpls/integration_tests/MODULE.bazel index 4aac419f..134751e3 100644 --- a/starpls/integration_tests/MODULE.bazel +++ b/starpls/integration_tests/MODULE.bazel @@ -49,14 +49,14 @@ local_path_override( bazel_dep(name = "trlc", version = "0.0.0") git_override( module_name = "trlc", - commit = "c7750ebd0520996e05b9a9bc91eb6a4f62030f22", + commit = "1872957ae981ae632a0ec6eafb5b8d3aa44bdb86", remote = "https://github.com/bmw-software-engineering/trlc.git", ) bazel_dep(name = "lobster", version = "0.0.0") git_override( module_name = "lobster", - commit = "56881461f9d3fde2918d1731aa5937aaf64cd67c", + commit = "94ed5961ca28ee1b840cd8a938138c17ae4da671", remote = "https://github.com/bmw-software-engineering/lobster.git", )