diff --git a/bazel/rules/score_module/BUILD b/bazel/rules/score_module/BUILD new file mode 100644 index 0000000..e69de29 diff --git a/bazel/rules/score_module/docs/conf.py b/bazel/rules/score_module/docs/conf.py new file mode 100644 index 0000000..a393a5b --- /dev/null +++ b/bazel/rules/score_module/docs/conf.py @@ -0,0 +1,52 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "SCORE MODULE API" +project_url = "https://eclipse-score.github.io/module_template/" +project_prefix = "MODULE_TEMPLATE_" +author = "S-CORE" +version = "0.1" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + + +extensions = [ + "sphinx_design", + "sphinx_needs", + "sphinxcontrib.plantuml", + "score_plantuml", + "score_metamodel", + "score_draw_uml_funcs", + "score_source_code_linker", + "score_layout", +] + +exclude_patterns = [ + "bazel-*", + ".venv_docs", +] + +templates_path = ["templates"] + +# Enable numref +numfig = True diff --git a/bazel/rules/score_module/docs/index.rst b/bazel/rules/score_module/docs/index.rst new file mode 100644 index 0000000..3c55316 --- /dev/null +++ b/bazel/rules/score_module/docs/index.rst @@ -0,0 +1,331 @@ +SCORE Module Bazel Rules +========================= + +This directory contains Bazel rules for defining and building SCORE safety modules. + +.. contents:: Table of Contents + :depth: 2 + :local: + + +Overview +-------- + +The ``score_module`` package provides Bazel build rules to structure, +validate, and document safety-critical software modules. These rules +integrate with Sphinx documentation generation to produce comprehensive +safety documentation. + +.. uml:: + + @startuml + [SEooC] as SEooC + [bazel module] as bzlmod + Artifact "Assumptions of Use" as AoU + Artifact "(Assumed) Component Requirements" as CR <> + Artifact "Architecture Design" as AD <> + Artifact "Safety Analysis" as SA <> + Card "Implementation" as Impl <> + Card "Testsuite" as Test <> + + + + bzlmod "1" *-- "*" SEooC : contains + SEooC ..> SEooC : depends on + SEooC "1" *-- "1" AoU : has + SEooC "1" *-- "1" CR : has + SEooC "1" *-- "1" AD : has + SEooC "1" *-- "1" Impl : has + SEooC "1" *-- "1" Test : has + SEooC "1" *-- "1" SA : has + + note right of bzlmod + A score_module can contain + one or more Safety Elements + out of Context (SEooC) + end note + + @enduml + + + + + +Rules and Macros +---------------- + +safety_element_out_of_context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**File:** ``score_module.bzl`` + +**Purpose:** Main macro for defining a Safety Element out of Context +(SEooC) module with integrated documentation generation following ISO 26262 +standards. + +**Usage:** + +.. code-block:: python + + safety_element_out_of_context( + name = "my_module", + assumptions_of_use = ":assumptions", + component_requirements = ":requirements", + architectural_design = ":architecture", + safety_analysis = ":safety_analysis", + implementations = [":my_lib", ":my_component"], + tests = [":my_lib_test", ":my_integration_test"], + visibility = ["//visibility:public"] + ) + +**Parameters:** + +- ``name``: The name of the safety element module. Used as the base name + for all generated targets. +- ``assumptions_of_use``: Label to a ``.rst`` or ``.md`` file containing the + Assumptions of Use, which define the safety-relevant operating conditions + and constraints for the SEooC as required by ISO 26262-10 clause 5.4.4. +- ``component_requirements``: Label to a ``.rst`` or ``.md`` file containing + the component requirements specification, defining functional and safety + requirements. +- ``architectural_design``: Label to a ``.rst`` or ``.md`` file containing + the architectural design specification, describing the software architecture + and design decisions as required by ISO 26262-6 clause 7. +- ``safety_analysis``: Label to a ``.rst`` or ``.md`` file containing the + safety analysis, including FMEA, FMEDA, FTA, or other safety analysis + results as required by ISO 26262-9 clause 8. Documents hazard analysis and + safety measures. +- ``implementations``: List of labels to Bazel targets representing the actual + software implementation (cc_library, cc_binary, etc.) that realizes the + component requirements. This is the source code that implements the safety + functions as required by ISO 26262-6 clause 8. +- ``tests``: List of labels to Bazel test targets (cc_test, py_test, etc.) + that verify the implementation against requirements. Includes unit tests and + integration tests as required by ISO 26262-6 clause 9 for software unit + verification. +- ``visibility``: Bazel visibility specification for the generated SEooC + target. Controls which other packages can depend on this safety element. + +**Generated Targets:** + +This macro creates multiple targets automatically: + +1. ``_index``: Generates index.rst and conf.py files for the module + documentation +2. ``_seooc_index_lib``: Sphinx documentation library for the + generated index +3. ````: The main SEooC target that aggregates all documentation +4. ``.html``: Convenience target to build HTML documentation + +**Implementation Details:** + +The macro orchestrates several internal rules to: + +- Generate a documentation index with a structured table of contents +- Organize documentation files under ``docs/safety_elements//`` +- Integrate assumptions of use and component requirements into a unified documentation structure +- Provide Sphinx-compatible output for HTML generation + +Private Rules +------------- + +seooc +~~~~~ + +**File:** ``private/seooc.bzl`` + +**Purpose:** Internal rule that aggregates safety documentation artifacts +into a Sphinx-compatible structure. + +**Implementation:** ``_seooc_build_impl`` + +**Functionality:** + +- Collects documentation from ``assumptions_of_use`` and + ``component_requirements`` dependencies +- Reorganizes file paths to place artifacts under + ``docs/safety_elements//`` +- Merges the generated index with artifact documentation +- Returns a ``SphinxDocsLibraryInfo`` provider for downstream consumption + +**Attributes:** + +- ``assumptions_of_use``: Label to assumptions of use documentation (mandatory) +- ``component_requirements``: Label to component requirements documentation (mandatory) +- ``index``: Label to the generated index file (mandatory) + +**Output:** + +Returns a ``SphinxDocsLibraryInfo`` provider containing: + +- ``transitive``: Depset of documentation file structs with relocated paths +- ``files``: Empty list (files are in transitive dependencies) + +seooc_sphinx_environment +~~~~~~~~~~~~~~~~~~~~~~~~ + +**File:** ``private/seooc_index.bzl`` + +**Purpose:** Generates the Sphinx environment files (index.rst and conf.py) +for a safety module. + +**Implementation:** ``_seooc_sphinx_environment_impl`` + +**Functionality:** + +- Creates a module-specific ``index.rst`` with: + + - Module name as header (uppercase with underline) + - Table of contents (toctree) linking to all safety artifacts + - References to assumptions of use and component requirements index files + +- Generates a ``conf.py`` configuration file (currently placeholder content) + +**Attributes:** + +- ``module_name``: String name of the module (used for header generation) +- ``assumptions_of_use``: Label to assumptions documentation +- ``component_requirements``: Label to requirements documentation + +**Generated Content Example:** + +.. code-block:: rst + + MY_MODULE + ========= + + .. toctree:: + :maxdepth: 2 + :caption: Contents: + + assumptions_of_use/index + component_requirements/index + architectural_design/index + safety_analysis/index + +**Output:** + +Returns ``DefaultInfo`` with generated ``index.rst`` files. + +Documentation Structure +----------------------- + +When using these rules, documentation is organized in the Bazel sandbox as +follows:: + + docs/ + └── safety_elements/ + └── / + ├── index.rst (generated) + ├── conf.py (generated) + ├── assumptions_of_use/ + │ └── (user-provided documentation) + ├── component_requirements/ + │ └── (user-provided documentation) + ├── architectural_design/ + │ └── (user-provided documentation) + └── safety_analysis/ + └── (user-provided documentation) + + bazel-bin/ + └── / + └── _sources/ + └── (generated documentation sources) + +This structure reflects the file organization created in the Bazel sandbox +during the documentation generation process. The generated ``index.rst`` file +includes a table of contents that references all provided artifacts. + +Integration with Sphinx +------------------------ + +The rules generate ``SphinxDocsLibraryInfo`` providers that are compatible +with ``@rules_python//sphinxdocs``. This enables: + +- Automatic discovery of documentation files by Sphinx +- Proper path relocation for modular documentation +- Transitive dependency handling across multiple safety modules +- HTML, PDF, and other Sphinx output formats + +Usage Example +------------- + +Complete example in a BUILD file: + +.. code-block:: python + + load("@baselibs//bazel/score_module:score_module.bzl", + "safety_element_out_of_context") + + # Documentation artifacts + sphinx_docs_library( + name = "assumptions", + srcs = ["docs/assumptions_of_use.rst"], + ) + + sphinx_docs_library( + name = "requirements", + srcs = ["docs/component_requirements.rst"], + ) + + sphinx_docs_library( + name = "architecture", + srcs = ["docs/architectural_design.rst"], + ) + + sphinx_docs_library( + name = "safety", + srcs = ["docs/safety_analysis.rst"], + ) + + # Implementation targets + cc_library( + name = "lifecycle_lib", + srcs = ["lifecycle_manager.cpp"], + hdrs = ["lifecycle_manager.h"], + ) + + # Test targets + cc_test( + name = "lifecycle_test", + srcs = ["lifecycle_manager_test.cpp"], + deps = [":lifecycle_lib"], + ) + + # Safety Element out of Context + safety_element_out_of_context( + name = "lifecycle_manager_seooc", + assumptions_of_use = ":assumptions", + component_requirements = ":requirements", + architectural_design = ":architecture", + safety_analysis = ":safety", + implementations = [":lifecycle_lib"], + tests = [":lifecycle_test"], + visibility = ["//visibility:public"], + ) + +Then build the documentation: + +.. code-block:: bash + + # Build the SEooC target + bazel build //:lifecycle_manager_seooc + + +Dependencies +------------ + +- ``@rules_python//sphinxdocs``: Sphinx documentation build rules +- ``SphinxDocsLibraryInfo``: Provider for documentation artifacts + +Design Rationale +---------------- + +These rules enforce a structured approach to safety documentation by: + +1. **Standardization**: All safety modules follow the same documentation + structure +2. **Traceability**: Build system ensures all required artifacts are present +3. **Modularity**: Documentation can be composed from multiple sources +4. **Automation**: Index generation and path management are automated +5. **Integration**: Seamless integration with existing Sphinx workflows diff --git a/bazel/rules/score_module/private/BUILD b/bazel/rules/score_module/private/BUILD new file mode 100644 index 0000000..e69de29 diff --git a/bazel/rules/score_module/private/seooc.bzl b/bazel/rules/score_module/private/seooc.bzl new file mode 100644 index 0000000..d0bfbe6 --- /dev/null +++ b/bazel/rules/score_module/private/seooc.bzl @@ -0,0 +1,82 @@ +load("@rules_python//sphinxdocs/private:sphinx_docs_library_info.bzl", "SphinxDocsLibraryInfo") + +seooc_artifacts = { + "assumptions_of_use": attr.label( + providers = [SphinxDocsLibraryInfo], + mandatory = True, + doc = "Label to a sphinx_docs_library target containing the Assumptions of Use, which define the safety-relevant operating conditions and constraints for the SEooC as required by ISO 26262-10 clause 5.4.4.", + ), + "component_requirements": attr.label( + providers = [SphinxDocsLibraryInfo], + mandatory = True, + doc = "Label to a sphinx_docs_library target containing the component requirements specification, defining functional and safety requirements as required by ISO 26262-3 clause 7.", + ), + "architectural_design": attr.label( + providers = [SphinxDocsLibraryInfo], + mandatory = True, + doc = "Label to a sphinx_docs_library target containing the architectural design specification, describing the software architecture and design decisions as required by ISO 26262-6 clause 7.", + ), + "safety_analysis": attr.label( + providers = [SphinxDocsLibraryInfo], + mandatory = True, + doc = "Label to a sphinx_docs_library target containing the safety analysis, including FMEA, FMEDA, FTA, or other safety analysis results as required by ISO 26262-9 clause 8. Documents hazard analysis and safety measures.", + ), +} + +seooc_targets = { + "implementations": attr.label( + mandatory = False, + doc = "", + ), + "tests": attr.label( + mandatory = False, + doc = "", + ), +} + +def _seooc_build_impl(ctx): + """Implementation of safety_element build rule for ISO 26262 SEooC.""" + + all_files = [] + for artifact in seooc_artifacts: + dep = getattr(ctx.attr, artifact) + for t in dep[SphinxDocsLibraryInfo].transitive.to_list(): + entry = struct( + strip_prefix = t.strip_prefix, + prefix = "docs/safety_elements/" + ctx.attr.name + "/" + t.prefix, + files = t.files, + ) + all_files.append(entry) + + index = ctx.attr.index + for t in index[SphinxDocsLibraryInfo].transitive.to_list(): + entry = struct( + strip_prefix = t.strip_prefix, + prefix = "", + files = t.files, + ) + all_files.append(entry) + + result = depset(all_files) + return [ + DefaultInfo( + files = depset([]), + ), + SphinxDocsLibraryInfo( + strip_prefix = "", + prefix = "", + files = [], + transitive = result, + ), + ] + +seooc = rule( + implementation = _seooc_build_impl, + attrs = seooc_artifacts | { + "index": attr.label( + allow_files = [".rst", ".md", ".py"], + mandatory = True, + doc = "", + ), + }, +) diff --git a/bazel/rules/score_module/private/seooc_sphinx_environment.bzl b/bazel/rules/score_module/private/seooc_sphinx_environment.bzl new file mode 100644 index 0000000..9d1738b --- /dev/null +++ b/bazel/rules/score_module/private/seooc_sphinx_environment.bzl @@ -0,0 +1,54 @@ +load("@rules_python//sphinxdocs/private:sphinx_docs_library_info.bzl", "SphinxDocsLibraryInfo") +load("//bazel/rules/score_module/private:seooc.bzl", "seooc_artifacts") + +index_content = """ + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + +""" + +def _seooc_sphinx_environment_impl(ctx): + """Generate the index.rst file for a seooc""" + + index_rst = ctx.actions.declare_file("docs/safety_elements/" + ctx.attr.module_name + "/index.rst") + + header = ctx.attr.module_name.upper() + header += "\n" + "=" * len(header) + + file_content = header + index_content + + for artifact in seooc_artifacts: + attr = getattr(ctx.attr, artifact) + if attr: + # Get all files from the SphinxDocsLibraryInfo + src_files = list(attr[SphinxDocsLibraryInfo].files) + if src_files: + # Use the first file (typically the main documentation file) + artifact_index_file = src_files[0] + + # Create link path from the file + link = artifact_index_file.short_path.replace(".rst", "").replace(".md", "") + if ctx.label.package: + print("replacing link: " + ctx.label.package + "/") + link = link.replace(ctx.label.package + "/", "") + file_content += " " + link + "\n" + + ctx.actions.write( + output = index_rst, + content = file_content, + ) + + return ( + DefaultInfo( + files = depset([index_rst]), + ) + ) + +seooc_sphinx_environment = rule( + implementation = _seooc_sphinx_environment_impl, + attrs = seooc_artifacts | { + "module_name": attr.string(), + }, +) diff --git a/bazel/rules/score_module/score_module.bzl b/bazel/rules/score_module/score_module.bzl new file mode 100644 index 0000000..6ed760a --- /dev/null +++ b/bazel/rules/score_module/score_module.bzl @@ -0,0 +1,80 @@ +load("@rules_python//sphinxdocs:sphinx.bzl", "sphinx_docs") +load("@rules_python//sphinxdocs:sphinx_docs_library.bzl", "sphinx_docs_library") +load("//bazel/rules/score_module/private:seooc.bzl", "seooc") +load("//bazel/rules/score_module/private:seooc_sphinx_environment.bzl", "seooc_sphinx_environment") + +def safety_element_out_of_context( + name, + assumptions_of_use, + component_requirements, + architectural_design, + safety_analysis, + implementations, + tests, + visibility): + """Defines a Safety Element out of Context (SEooC) following ISO 26262 standards. + + This macro creates a complete SEooC module with integrated documentation generation + using Sphinx. It packages all required ISO 26262 artifacts and generates HTML + documentation for safety certification. + + Args: + name: The name of the safety element module. Used as the base name for all + generated targets. + assumptions_of_use: Label to a .rst or .md file containing the Assumptions of Use, + which define the safety-relevant operating conditions and constraints for the + SEooC as required by ISO 26262-10 clause 5.4.4. + component_requirements: Label to a .rst or .md file containing the component + requirements specification, defining functional and safety requirements as + required by ISO 26262-3 clause 7. + architectural_design: Label to a .rst or .md file containing the architectural + design specification, describing the software architecture and design decisions + as required by ISO 26262-6 clause 7. + safety_analysis: Label to a .rst or .md file containing the safety analysis, + including FMEA, FMEDA, FTA, or other safety analysis results as required by + ISO 26262-9 clause 8. Documents hazard analysis and safety measures. + implementations: List of labels to Bazel targets representing the actual software + implementation (cc_library, cc_binary, etc.) that realizes the component + requirements. This is the source code that implements the safety functions + as required by ISO 26262-6 clause 8. + tests: List of labels to Bazel test targets (cc_test, py_test, etc.) that verify + the implementation against requirements. Includes unit tests and integration + tests as required by ISO 26262-6 clause 9 for software unit verification. + visibility: Bazel visibility specification for the generated SEooC target. Controls + which other packages can depend on this safety element. + + Generated Targets: + _index: Sphinx environment with generated index.rst and conf.py files + _seooc_index_lib: Sphinx documentation library for the module + : Main SEooC target aggregating all documentation + .html: HTML documentation output + """ + + # Generate index file for the seooc documentation + seooc_sphinx_environment( + name = name + "_index", + module_name = name, + assumptions_of_use = assumptions_of_use, + component_requirements = component_requirements, + architectural_design = architectural_design, + safety_analysis = safety_analysis, + ) + + sphinx_docs_library( + name = name + "_seooc_index_lib", + srcs = [name + "_index"], + prefix = "", + visibility = ["//visibility:public"], + deps = [], + ) + + # Create the main SEooC target + seooc( + name = name, + index = name + "_seooc_index_lib", + assumptions_of_use = assumptions_of_use, + component_requirements = component_requirements, + architectural_design = architectural_design, + safety_analysis = safety_analysis, + visibility = visibility, + ) diff --git a/bazel/rules/score_module/test/BUILD b/bazel/rules/score_module/test/BUILD new file mode 100644 index 0000000..9168458 --- /dev/null +++ b/bazel/rules/score_module/test/BUILD @@ -0,0 +1,80 @@ +# ******************************************************************************* +# 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//sphinxdocs:sphinx_docs_library.bzl", "sphinx_docs_library") +load("//bazel/rules/score_module:score_module.bzl", "safety_element_out_of_context") +load("//bazel/rules/score_module/private:seooc.bzl", "seooc") +load(":score_module_test.bzl", "safety_element_macro_test_suite") +load(":seooc_test.bzl", "seooc_test_suite") + +package(default_visibility = ["//visibility:public"]) + +# Test fixtures for seooc tests +sphinx_docs_library( + name = "test_assumptions", + srcs = ["fixtures/assumptions_of_use.rst"], + tags = ["manual"], +) + +sphinx_docs_library( + name = "test_requirements", + srcs = ["fixtures/component_requirements.rst"], + tags = ["manual"], +) + +sphinx_docs_library( + name = "test_architecture", + srcs = ["fixtures/architecture_design.rst"], + tags = ["manual"], +) + +sphinx_docs_library( + name = "test_safety", + srcs = ["fixtures/safety_analysis.rst"], + tags = ["manual"], +) + +sphinx_docs_library( + name = "test_index_lib", + srcs = ["fixtures/index.rst"], + tags = ["manual"], +) + +# Minimal seooc test target with all mandatory attributes +seooc( + name = "test_seooc_minimal", + architectural_design = ":test_architecture", + assumptions_of_use = ":test_assumptions", + component_requirements = ":test_requirements", + index = ":test_index_lib", + safety_analysis = ":test_safety", + tags = ["manual"], +) + +# Run the test suite +seooc_test_suite(name = "seooc_tests") + +# Test target using the safety_element_out_of_context macro +# Uses sphinx_docs_library targets that provide SphinxDocsLibraryInfo +safety_element_out_of_context( + name = "test_macro_seooc", + architectural_design = ":test_architecture", + assumptions_of_use = ":test_assumptions", + component_requirements = ":test_requirements", + implementations = None, + safety_analysis = ":test_safety", + tests = None, + visibility = ["//visibility:public"], +) + +# Run the macro test suite +safety_element_macro_test_suite(name = "macro_tests") diff --git a/bazel/rules/score_module/test/README.md b/bazel/rules/score_module/test/README.md new file mode 100644 index 0000000..f85cdc9 --- /dev/null +++ b/bazel/rules/score_module/test/README.md @@ -0,0 +1,137 @@ +# SEooC Rule Tests + +This directory contains unit tests for the `seooc` rule, which is used to define Safety Elements out of Context (SEooC) following ISO 26262 standards. + +## Test Suite Overview + +The test suite (`seooc_test.bzl`) uses Bazel Skylib's `unittest` framework to verify the correctness of the `seooc` rule implementation. The tests are organized into several categories: + +### Test Categories + +1. **Provider Tests** (`seooc_providers_test`) + - Verifies that the `seooc` rule provides the required providers + - Checks for `DefaultInfo` provider + - Checks for `SphinxDocsLibraryInfo` provider + +2. **Transitive Documentation Tests** (`seooc_transitive_docs_test`) + - Verifies that the rule correctly aggregates transitive documentation + - Ensures that the transitive field is a depset + - Validates the structure of transitive documentation entries + - Confirms that documentation paths start with `docs/safety_elements/` + +3. **Attribute Handling Tests** (`seooc_attributes_test`) + - Verifies that mandatory attributes are correctly handled + - Ensures that at least index documentation is present + +4. **Path Prefixing Tests** (`seooc_path_prefixing_test`) + - Verifies that documentation paths are correctly prefixed with the module name + - Ensures proper path organization for Sphinx documentation + +## Test Fixtures + +The test suite uses the following fixture files located in `fixtures/`: + +- **`assumptions_of_use.rst`**: Sample assumptions of use document +- **`component_requirements.rst`**: Sample component requirements document +- **`index.rst`**: Sample index file for documentation structure + +These fixtures are wrapped as `sphinx_docs_library` targets in the `BUILD` file to create realistic test scenarios. + +## Running the Tests + +To run all seooc tests: + +```bash +bazel test //bazel/rules/score_module/test:seooc_tests +``` + +To run with verbose output: + +```bash +bazel test //bazel/rules/score_module/test:seooc_tests --test_output=all +``` + +To run individual tests: + +```bash +bazel test //bazel/rules/score_module/test:seooc_providers_test +bazel test //bazel/rules/score_module/test:seooc_transitive_docs_test +bazel test //bazel/rules/score_module/test:seooc_attributes_test +bazel test //bazel/rules/score_module/test:seooc_path_prefixing_test +``` + +## Test Targets + +The `BUILD` file defines two test targets: + +1. **`test_seooc_minimal`**: Tests the seooc rule with minimal required attributes + - Only includes `assumptions_of_use` and `index` + - Verifies basic functionality + +2. **`test_seooc_complete`**: Tests the seooc rule with all optional attributes + - Includes all optional documentation attributes + - Verifies handling of complete documentation sets + +## Adding New Tests + +To add a new test: + +1. Define a test implementation function in `seooc_test.bzl`: + + ```python + def _my_new_test_impl(ctx): + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Your test assertions here + asserts.true(env, condition, "error message") + + return analysistest.end(env) + + my_new_test = analysistest.make(_my_new_test_impl) + ``` + +2. Add the test to `_test_seooc()` function: + + ```python + my_new_test( + name = "my_new_test", + target_under_test = ":test_seooc_minimal", + ) + ``` + +3. Include it in the test suite: + + ```python + native.test_suite( + name = name, + tests = [ + # ... existing tests ... + ":my_new_test", + ], + ) + ``` + +## Test Coverage + +The current test suite covers: + +- ✅ Provider generation +- ✅ Transitive documentation aggregation +- ✅ Mandatory attribute handling +- ✅ Path prefixing with module names +- ✅ Optional attribute handling + +## Dependencies + +The tests depend on: + +- `@bazel_skylib//lib:unittest` - Bazel Skylib testing framework +- `@rules_python//sphinxdocs` - Sphinx documentation rules +- `//bazel/rules/score_module/private:seooc.bzl` - The rule being tested + +## Notes + +- All test targets are tagged with `"manual"` to prevent them from being built during normal builds +- Tests use the `analysistest` framework, which performs analysis-phase validation +- The test suite is part of the continuous integration pipeline diff --git a/bazel/rules/score_module/test/fixtures/architecture_design.rst b/bazel/rules/score_module/test/fixtures/architecture_design.rst new file mode 100644 index 0000000..d3665d0 --- /dev/null +++ b/bazel/rules/score_module/test/fixtures/architecture_design.rst @@ -0,0 +1,46 @@ +Architecture Design +=================== + +This document describes the architectural design of the test SEooC module. + +Software Architecture +--------------------- + +The system consists of the following components: + +* Input Processing Module +* Data Processing Engine +* Output Handler +* Fault Detection and Handling + +Component Interfaces +--------------------- + +Input Processing Module +~~~~~~~~~~~~~~~~~~~~~~~ + +* **Input**: Raw sensor data +* **Output**: Validated and formatted data +* **Interface**: I2C/SPI bus + +Data Processing Engine +~~~~~~~~~~~~~~~~~~~~~~ + +* **Input**: Validated data from Input Processing Module +* **Output**: Processed results +* **Interface**: Internal memory-mapped registers + +Design Decisions +---------------- + +Decision 1: Use of Hardware Watchdog +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The architecture includes a hardware watchdog timer to ensure system reliability +and meet safety requirements REQ-SAFE-001. + +Decision 2: Redundant Processing Paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Critical calculations are performed using redundant processing paths to detect +and prevent silent data corruption. diff --git a/bazel/rules/score_module/test/fixtures/assumptions_of_use.rst b/bazel/rules/score_module/test/fixtures/assumptions_of_use.rst new file mode 100644 index 0000000..11f3785 --- /dev/null +++ b/bazel/rules/score_module/test/fixtures/assumptions_of_use.rst @@ -0,0 +1,18 @@ +Assumptions of Use +================== + +This document describes the assumptions of use for the test SEooC module. + +Operating Conditions +-------------------- + +* Operating temperature: -40°C to +85°C +* Supply voltage: 12V ±10% +* Maximum processing load: 80% + +Environmental Assumptions +------------------------- + +* The system operates in a controlled environment +* No exposure to extreme weather conditions +* Regular maintenance is performed diff --git a/bazel/rules/score_module/test/fixtures/component_requirements.rst b/bazel/rules/score_module/test/fixtures/component_requirements.rst new file mode 100644 index 0000000..46c38ca --- /dev/null +++ b/bazel/rules/score_module/test/fixtures/component_requirements.rst @@ -0,0 +1,30 @@ +Component Requirements +====================== + +This document defines the functional and safety requirements. + +Functional Requirements +------------------------ + +REQ-FUNC-001 +~~~~~~~~~~~~ + +The system shall process input data within 100ms. + +REQ-FUNC-002 +~~~~~~~~~~~~ + +The system shall provide output with 99.9% accuracy. + +Safety Requirements +------------------- + +REQ-SAFE-001 +~~~~~~~~~~~~ + +The system shall detect and handle fault conditions within 50ms. + +REQ-SAFE-002 +~~~~~~~~~~~~ + +The system shall maintain safe state during power loss. diff --git a/bazel/rules/score_module/test/fixtures/index.rst b/bazel/rules/score_module/test/fixtures/index.rst new file mode 100644 index 0000000..01c37cf --- /dev/null +++ b/bazel/rules/score_module/test/fixtures/index.rst @@ -0,0 +1,13 @@ +Test Safety Element +=================== + +This is a test index file for the SEooC test suite. + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + + assumptions_of_use + component_requirements diff --git a/bazel/rules/score_module/test/fixtures/safety_analysis.rst b/bazel/rules/score_module/test/fixtures/safety_analysis.rst new file mode 100644 index 0000000..54b8908 --- /dev/null +++ b/bazel/rules/score_module/test/fixtures/safety_analysis.rst @@ -0,0 +1,57 @@ +Safety Analysis +=============== + +This document contains the safety analysis for the test SEooC module. + +Failure Mode and Effects Analysis (FMEA) +----------------------------------------- + +FMEA-001: Input Data Corruption +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* **Failure Mode**: Corrupted input data from sensors +* **Effect**: Incorrect processing results +* **Severity**: High +* **Detection Method**: CRC checksum validation +* **Mitigation**: Reject invalid data and enter safe state + +FMEA-002: Processing Timeout +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* **Failure Mode**: Processing exceeds time deadline +* **Effect**: System becomes unresponsive +* **Severity**: Medium +* **Detection Method**: Watchdog timer +* **Mitigation**: System reset and recovery + +Fault Tree Analysis (FTA) +-------------------------- + +Top Event: System Failure +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following events can lead to system failure: + +* Hardware failure (probability: 1e-6) +* Software defect (probability: 1e-5) +* External interference (probability: 1e-7) + +**Total failure probability**: 1.11e-5 per hour + +Safety Measures +--------------- + +SM-001: Input Validation +~~~~~~~~~~~~~~~~~~~~~~~~~ + +All input data is validated before processing to prevent invalid data propagation. + +SM-002: Periodic Self-Test +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The system performs periodic self-tests to detect latent faults. + +SM-003: Safe State Transition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Upon detection of critical faults, the system transitions to a predefined safe state. diff --git a/bazel/rules/score_module/test/score_module_test.bzl b/bazel/rules/score_module/test/score_module_test.bzl new file mode 100644 index 0000000..46092b9 --- /dev/null +++ b/bazel/rules/score_module/test/score_module_test.bzl @@ -0,0 +1,187 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +"""Unit tests for the safety_element_out_of_context macro.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("@rules_python//sphinxdocs/private:sphinx_docs_library_info.bzl", "SphinxDocsLibraryInfo") + +# Test that the macro generates the expected targets +def _macro_generates_targets_test_impl(ctx): + """Test that safety_element_out_of_context macro generates all expected targets.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # The main target should exist and provide the required providers + asserts.true( + env, + DefaultInfo in target_under_test, + "Main target should provide DefaultInfo", + ) + + asserts.true( + env, + SphinxDocsLibraryInfo in target_under_test, + "Main target should provide SphinxDocsLibraryInfo", + ) + + return analysistest.end(env) + +macro_generates_targets_test = analysistest.make(_macro_generates_targets_test_impl) + +# Test that the macro correctly creates the index library target +def _macro_index_lib_test_impl(ctx): + """Test that the macro creates the index library target.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + + # Verify that transitive documentation includes the index + transitive_list = sphinx_info.transitive.to_list() + asserts.true( + env, + len(transitive_list) > 0, + "Macro should generate documentation with index", + ) + + return analysistest.end(env) + +macro_index_lib_test = analysistest.make(_macro_index_lib_test_impl) + +# Test that the macro properly aggregates all documentation artifacts +def _macro_doc_aggregation_test_impl(ctx): + """Test that the macro aggregates all documentation artifacts.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + transitive_list = sphinx_info.transitive.to_list() + + # Should have documentation entries for: + # - index + # - assumptions_of_use + # - component_requirements + # - architectural_design + # - safety_analysis + # That's at least 5 entries (could be more with nested dependencies) + asserts.true( + env, + len(transitive_list) >= 5, + "Macro should aggregate all documentation artifacts (expected at least 5, got {})".format(len(transitive_list)), + ) + + return analysistest.end(env) + +macro_doc_aggregation_test = analysistest.make(_macro_doc_aggregation_test_impl) + +# Test that the macro correctly prefixes documentation paths +def _macro_path_structure_test_impl(ctx): + """Test that the macro creates correct documentation path structure.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + transitive_list = sphinx_info.transitive.to_list() + + # Extract the module name from the target label + module_name = target_under_test.label.name + + # Check that documentation paths follow the expected structure + found_correct_structure = False + for entry in transitive_list: + if "docs/safety_elements/" in entry.prefix and module_name in entry.prefix: + found_correct_structure = True + break + + asserts.true( + env, + found_correct_structure, + "Documentation paths should follow 'docs/safety_elements//' structure", + ) + + return analysistest.end(env) + +macro_path_structure_test = analysistest.make(_macro_path_structure_test_impl) + +# Test that the macro handles visibility correctly +def _macro_visibility_test_impl(ctx): + """Test that the macro correctly handles visibility attribute.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # If the target can be accessed in the test, visibility is working + asserts.true( + env, + target_under_test != None, + "Target should be accessible according to visibility settings", + ) + + return analysistest.end(env) + +macro_visibility_test = analysistest.make(_macro_visibility_test_impl) + +# Test suite setup function +def _test_safety_element_macro(): + """Creates test targets for the safety_element_out_of_context macro.""" + + # Test 1: Verify macro generates expected targets + macro_generates_targets_test( + name = "macro_generates_targets_test", + target_under_test = ":test_macro_seooc", + ) + + # Test 2: Verify index library generation + macro_index_lib_test( + name = "macro_index_lib_test", + target_under_test = ":test_macro_seooc", + ) + + # Test 3: Verify documentation aggregation + macro_doc_aggregation_test( + name = "macro_doc_aggregation_test", + target_under_test = ":test_macro_seooc", + ) + + # Test 4: Verify path structure + macro_path_structure_test( + name = "macro_path_structure_test", + target_under_test = ":test_macro_seooc", + ) + + # Test 5: Verify visibility handling + macro_visibility_test( + name = "macro_visibility_test", + target_under_test = ":test_macro_seooc", + ) + +def safety_element_macro_test_suite(name): + """Creates a test suite for the safety_element_out_of_context macro. + + Args: + name: The name of the test suite. + """ + _test_safety_element_macro() + + native.test_suite( + name = name, + tests = [ + ":macro_generates_targets_test", + ":macro_index_lib_test", + ":macro_doc_aggregation_test", + ":macro_path_structure_test", + ":macro_visibility_test", + ], + ) diff --git a/bazel/rules/score_module/test/seooc_test.bzl b/bazel/rules/score_module/test/seooc_test.bzl new file mode 100644 index 0000000..a417fed --- /dev/null +++ b/bazel/rules/score_module/test/seooc_test.bzl @@ -0,0 +1,345 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +"""Unit tests for the seooc rule.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("@rules_python//sphinxdocs/private:sphinx_docs_library_info.bzl", "SphinxDocsLibraryInfo") + +# Test that the seooc rule creates the correct providers +def _seooc_providers_test_impl(ctx): + """Test that seooc rule provides DefaultInfo and SphinxDocsLibraryInfo.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Check that DefaultInfo is provided + asserts.true( + env, + DefaultInfo in target_under_test, + "seooc rule should provide DefaultInfo", + ) + + # Check that SphinxDocsLibraryInfo is provided + asserts.true( + env, + SphinxDocsLibraryInfo in target_under_test, + "seooc rule should provide SphinxDocsLibraryInfo", + ) + + return analysistest.end(env) + +seooc_providers_test = analysistest.make(_seooc_providers_test_impl) + +# Test that the seooc rule correctly aggregates transitive documentation +def _seooc_transitive_docs_test_impl(ctx): + """Test that seooc rule correctly aggregates transitive documentation.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + + # Check that transitive field is a depset + asserts.true( + env, + type(sphinx_info.transitive) == type(depset([])), + "SphinxDocsLibraryInfo.transitive should be a depset", + ) + + # Check that transitive documentation is aggregated + transitive_list = sphinx_info.transitive.to_list() + asserts.true( + env, + len(transitive_list) > 0, + "seooc should aggregate transitive documentation", + ) + + # Verify that each entry has required fields + for entry in transitive_list: + asserts.true( + env, + hasattr(entry, "strip_prefix"), + "Each transitive entry should have strip_prefix field", + ) + asserts.true( + env, + hasattr(entry, "prefix"), + "Each transitive entry should have prefix field", + ) + asserts.true( + env, + hasattr(entry, "files"), + "Each transitive entry should have files field", + ) + + # Check that prefix either starts with the expected path or is empty (for index) + asserts.true( + env, + entry.prefix.startswith("docs/safety_elements/") or entry.prefix == "", + "Documentation prefix should start with 'docs/safety_elements/' or be empty for index", + ) + + return analysistest.end(env) + +seooc_transitive_docs_test = analysistest.make(_seooc_transitive_docs_test_impl) + +# Test that the seooc rule correctly handles mandatory and optional attributes +def _seooc_attributes_test_impl(ctx): + """Test that seooc rule correctly handles mandatory attributes.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + + # Verify that documentation files are present + transitive_list = sphinx_info.transitive.to_list() + + # There should be at least documentation from the index + asserts.true( + env, + len(transitive_list) >= 1, + "seooc should have at least index documentation", + ) + + return analysistest.end(env) + +seooc_attributes_test = analysistest.make(_seooc_attributes_test_impl) + +# Test that seooc properly prefixes paths with module name +def _seooc_path_prefixing_test_impl(ctx): + """Test that seooc rule correctly prefixes paths with module name.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + transitive_list = sphinx_info.transitive.to_list() + + # Extract the module name from the target label + module_name = target_under_test.label.name + + # Check that at least one entry has the correct prefix + found_correct_prefix = False + for entry in transitive_list: + if module_name in entry.prefix: + found_correct_prefix = True + break + + asserts.true( + env, + found_correct_prefix, + "At least one documentation entry should contain the module name in its prefix", + ) + + return analysistest.end(env) + +seooc_path_prefixing_test = analysistest.make(_seooc_path_prefixing_test_impl) + +# Test that seooc properly processes assumptions_of_use attribute +def _seooc_has_assumptions_test_impl(ctx): + """Test that seooc rule properly processes assumptions_of_use attribute.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + transitive_list = sphinx_info.transitive.to_list() + + # Verify that documentation is present (should include assumptions) + asserts.true( + env, + len(transitive_list) > 0, + "seooc should include assumptions_of_use documentation", + ) + + return analysistest.end(env) + +seooc_has_assumptions_test = analysistest.make(_seooc_has_assumptions_test_impl) + +# Test that seooc properly processes component_requirements attribute +def _seooc_has_requirements_test_impl(ctx): + """Test that seooc rule properly processes component_requirements attribute.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + transitive_list = sphinx_info.transitive.to_list() + + # Verify that documentation is present (should include requirements) + asserts.true( + env, + len(transitive_list) > 0, + "seooc should include component_requirements documentation", + ) + + return analysistest.end(env) + +seooc_has_requirements_test = analysistest.make(_seooc_has_requirements_test_impl) + +# Test that seooc properly processes architectural_design attribute +def _seooc_has_architecture_test_impl(ctx): + """Test that seooc rule properly processes architectural_design attribute.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + transitive_list = sphinx_info.transitive.to_list() + + # Verify that documentation is present (should include architecture) + asserts.true( + env, + len(transitive_list) > 0, + "seooc should include architectural_design documentation", + ) + + return analysistest.end(env) + +seooc_has_architecture_test = analysistest.make(_seooc_has_architecture_test_impl) + +# Test that seooc properly processes safety_analysis attribute +def _seooc_has_safety_test_impl(ctx): + """Test that seooc rule properly processes safety_analysis attribute.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + transitive_list = sphinx_info.transitive.to_list() + + # Verify that documentation is present (should include safety analysis) + asserts.true( + env, + len(transitive_list) > 0, + "seooc should include safety_analysis documentation", + ) + + return analysistest.end(env) + +seooc_has_safety_test = analysistest.make(_seooc_has_safety_test_impl) + +# Test that seooc properly processes index attribute +def _seooc_has_index_test_impl(ctx): + """Test that seooc rule properly processes index attribute.""" + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + + # Get the SphinxDocsLibraryInfo provider + sphinx_info = target_under_test[SphinxDocsLibraryInfo] + transitive_list = sphinx_info.transitive.to_list() + + # Verify that documentation is present (should include index) + asserts.true( + env, + len(transitive_list) > 0, + "seooc should include index documentation", + ) + + return analysistest.end(env) + +seooc_has_index_test = analysistest.make(_seooc_has_index_test_impl) + +# Test suite setup function +def _test_seooc(): + """Creates test targets for the seooc rule.""" + + # Test 1: Verify providers + seooc_providers_test( + name = "seooc_providers_test", + target_under_test = ":test_seooc_minimal", + ) + + # Test 2: Verify transitive documentation aggregation + seooc_transitive_docs_test( + name = "seooc_transitive_docs_test", + target_under_test = ":test_seooc_minimal", + ) + + # Test 3: Verify attribute handling + seooc_attributes_test( + name = "seooc_attributes_test", + target_under_test = ":test_seooc_minimal", + ) + + # Test 4: Verify path prefixing + seooc_path_prefixing_test( + name = "seooc_path_prefixing_test", + target_under_test = ":test_seooc_minimal", + ) + + # Test with complete attributes + seooc_providers_test( + name = "seooc_providers_test_complete", + target_under_test = ":test_seooc_complete", + ) + + seooc_transitive_docs_test( + name = "seooc_transitive_docs_test_complete", + target_under_test = ":test_seooc_complete", + ) + + # Test 5: Verify assumptions_of_use attribute is processed + seooc_has_assumptions_test( + name = "seooc_has_assumptions_test", + target_under_test = ":test_seooc_complete", + ) + + # Test 6: Verify component_requirements attribute is processed + seooc_has_requirements_test( + name = "seooc_has_requirements_test", + target_under_test = ":test_seooc_complete", + ) + + # Test 7: Verify architectural_design attribute is processed + seooc_has_architecture_test( + name = "seooc_has_architecture_test", + target_under_test = ":test_seooc_complete", + ) + + # Test 8: Verify safety_analysis attribute is processed + seooc_has_safety_test( + name = "seooc_has_safety_test", + target_under_test = ":test_seooc_complete", + ) + + # Test 9: Verify index attribute is processed + seooc_has_index_test( + name = "seooc_has_index_test", + target_under_test = ":test_seooc_complete", + ) + +def seooc_test_suite(name): + """Creates a test suite for the seooc rule. + + Args: + name: The name of the test suite. + """ + _test_seooc() + + native.test_suite( + name = name, + tests = [ + ":seooc_providers_test", + ":seooc_transitive_docs_test", + ":seooc_attributes_test", + ":seooc_path_prefixing_test", + ":seooc_providers_test_complete", + ":seooc_transitive_docs_test_complete", + ":seooc_has_assumptions_test", + ":seooc_has_requirements_test", + ":seooc_has_architecture_test", + ":seooc_has_safety_test", + ":seooc_has_index_test", + ], + )