diff --git a/.github/tools/coverage.sh b/.github/tools/coverage.sh new file mode 100755 index 00000000..508521ec --- /dev/null +++ b/.github/tools/coverage.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env 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 +# ******************************************************************************* +# +# Runs unit + component tests with code coverage and generates HTML + +# Cobertura XML reports. +# +# Prerequisites (install once): +# sudo apt-get install -y lcov +# pipx install lcov-cobertura +# +# Usage: +# .github/tools/coverage.sh [] [--config ] [--output-dir ] +# +# Options: +# Bazel target to collect coverage for (default: //score/...) +# --config Bazel config to use (default: time-x86_64-linux) +# --output-dir Directory for generated reports (default: cpp_coverage) + +set -euo pipefail + +OUTPUT_DIR="cpp_coverage" +BAZEL_CONFIG="time-x86_64-linux" +BAZEL_TARGET="${1:-//score/...}" + +# Consume the target argument if it was provided positionally +[[ $# -gt 0 && "$1" != --* ]] && shift + +while [[ $# -gt 0 ]]; do + case "$1" in + --config) + BAZEL_CONFIG="$2" + shift 2 + ;; + --output-dir) + OUTPUT_DIR="$2" + shift 2 + ;; + --target) + BAZEL_TARGET="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +echo "==> Running tests with coverage..." +bazel coverage --config="${BAZEL_CONFIG}" -- "${BAZEL_TARGET}" + +OUTPUT_PATH="$(bazel info output_path)" +EXEC_ROOT="$(bazel info execution_root)" +DAT_FILE="${OUTPUT_PATH}/_coverage/_coverage_report.dat" + +echo "==> Generating HTML report in '${OUTPUT_DIR}'..." +genhtml "${DAT_FILE}" \ + --output-directory="${OUTPUT_DIR}" \ + --show-details \ + --source-directory="${EXEC_ROOT}" \ + --legend \ + --function-coverage \ + --branch-coverage + +echo "==> Generating Cobertura XML report at '${OUTPUT_DIR}/coverage.xml'..." +lcov_cobertura "${DAT_FILE}" \ + --base-dir "${EXEC_ROOT}" \ + --output "${OUTPUT_DIR}/coverage.xml" + +echo "" +echo "Coverage reports written to '${OUTPUT_DIR}/'." +echo " HTML: ${OUTPUT_DIR}/index.html" +echo " Cobertura: ${OUTPUT_DIR}/coverage.xml" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8e0f5b17..bbbcee8d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -49,3 +49,7 @@ jobs: # the bazel-target depends on your repo specific docs_targets configuration (e.g. "suffix") bazel-target: "//:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" retention-days: 3 + + quality_pack: + secrets: inherit + uses: ./.github/workflows/quality_pack.yml diff --git a/.github/workflows/quality_pack-publish.yml b/.github/workflows/quality_pack-publish.yml new file mode 100644 index 00000000..23d8f7a4 --- /dev/null +++ b/.github/workflows/quality_pack-publish.yml @@ -0,0 +1,123 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +# Waits for the Docs workflow of a pull request, downloads the traceability gate +# artifact produced by quality_pack.yml, and posts (or edits) a +# comment on the pull request with the gate summary and pass/fail status. +# +# Runs via workflow_run so that pull requests opened from forks — which get a +# read-only GITHUB_TOKEN and cannot write comments themselves — still get a +# comment posted by this workflow, which runs with the base repo's token. + +name: Publish Quality Pack Comment + +on: + workflow_run: + workflows: ["Docs / Build & Deploy"] + types: + - completed + +jobs: + quality-pack-comment: + name: Comment traceability gate on the pull request + if: github.event.workflow_run.event == 'pull_request' || github.event.workflow_run.event == 'pull_request_target' + runs-on: ubuntu-latest + permissions: + actions: read # list + download the artifacts of the triggering workflow run + pull-requests: write # comment on the pull request + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + RUN_ID: ${{ github.event.workflow_run.id }} + steps: + - name: Resolve pull request number + id: metadata + env: + HEAD_REPO_OWNER: ${{ github.event.workflow_run.head_repository.owner.login }} + PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number || '' }} + REF_NAME: ${{ github.event.workflow_run.head_branch }} + run: | + set -euo pipefail + + if [[ "$PR_NUMBER" == "null" ]]; then + PR_NUMBER="" + fi + + # fork-origin workflow_run payloads do not include pull_requests[] + if [[ -z "$PR_NUMBER" && -n "$HEAD_REPO_OWNER" && -n "$REF_NAME" ]]; then + PR_NUMBER="$(gh api \ + "repos/${REPO}/pulls?state=all&head=${HEAD_REPO_OWNER}:${REF_NAME}" \ + --jq '.[0].number // empty' || true)" + fi + + if [[ -z "$PR_NUMBER" ]]; then + echo "Could not determine PR number for pull request event." >&2 + exit 1 + fi + + echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT" + + - name: Download quality pack artifact + id: download + continue-on-error: true + uses: actions/download-artifact@v4 + with: + name: quality-pack-metrics + path: _quality_pack_dl + github-token: ${{ github.token }} + repository: ${{ github.repository }} + run-id: ${{ github.event.workflow_run.id }} + + - name: Render comment body + id: render + run: | + set -euo pipefail + + GATE_FILE=_quality_pack_dl/_quality_pack/gate.txt + if [[ -f "$GATE_FILE" ]]; then + if grep -q "Threshold check passed." "$GATE_FILE"; then + status="**PASS**" + else + status="**FAIL**" + fi + gate_body=$(cat "$GATE_FILE") + else + status="**UNAVAILABLE**" + gate_body="No traceability gate output was produced by the docs run." + fi + + { + echo "Quality pack traceability report for this pull request:" + echo + echo "Status: $status ([workflow run](${{ github.event.workflow_run.html_url }}))" + echo + echo '```text' + echo "$gate_body" + echo '```' + } > comment.md + + - name: Find existing PR comment + uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0 + id: fc + with: + issue-number: ${{ steps.metadata.outputs.pr_number }} + comment-author: "github-actions[bot]" + body-includes: Quality pack traceability report for this pull request + + - name: Comment on PR with traceability gate summary + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 + with: + issue-number: ${{ steps.metadata.outputs.pr_number }} + comment-id: ${{ steps.fc.outputs.comment-id }} + edit-mode: replace + body-path: comment.md diff --git a/.github/workflows/quality_pack.yml b/.github/workflows/quality_pack.yml new file mode 100644 index 00000000..e56ebc85 --- /dev/null +++ b/.github/workflows/quality_pack.yml @@ -0,0 +1,75 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +# Builds the docs and runs the upstream //:traceability_gate against the +# resulting _build/metrics.json, then uploads the gate output as an artifact. +# +# This workflow does NOT comment on the PR — see quality_pack-publish.yml for +# the elevated-permissions companion that downloads the artifact and posts. +# The split is required so that pull requests opened from forks (which get a +# read-only GITHUB_TOKEN) still get a comment. + +name: Quality pack coverage + +on: + workflow_call: + +jobs: + quality-pack: + runs-on: ubuntu-24.04 + steps: + - uses: eclipse-score/more-disk-space@v1 + - name: Checkout repository + uses: actions/checkout@v4 + - name: Create Bazel output base directory + run: | + sudo mkdir -p /mnt/.bazel + sudo chown -R $USER:$USER /mnt/.bazel + - name: Create config indicator file + run: echo "host" > .bazel_config + - name: Setup Bazel + uses: eclipse-score/cicd-actions/setup-bazel-cache@212bbf86267e9381da9d2daf962d12f6feafbc90 # v0.0.2 + with: + unique-cache-name: ${{ github.job }} + - name: Allow linux-sandbox + uses: eclipse-score/cicd-actions/unblock-user-namespace-for-linux-sandbox@212bbf86267e9381da9d2daf962d12f6feafbc90 # v0.0.2 + - name: Install Graphviz (required by PlantUML for non-sequence diagrams) + run: sudo apt-get update && sudo apt-get install -y graphviz + - name: Build unit + component tests (needed for test links) + run: bazel test //:unit_tests //:component_tests + - name: Build docs + run: bazel run //:docs + # Thresholds default to 0 while comp_req coverage is being (re-)built up + # against the SOME/IP protocol spec. Raise the --min-* flags as + # links land, so the gate never regresses without a follow-up ticket. + # --need-type=comp_req scopes the gate to component requirements; + # feat_req / stkh_req live upstream in eclipse-score/score and would + # otherwise pull the numbers to zero and flap the gate. + - name: Run traceability gate + continue-on-error: true + run: | + mkdir -p _quality_pack + bazel run //:traceability_gate -- \ + --metrics-json "$PWD/_build/metrics.json" \ + --need-type=comp_req \ + > _quality_pack/gate.txt + - name: Upload quality pack artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: quality-pack-metrics + path: | + _build/metrics.json + _quality_pack/gate.txt + if-no-files-found: warn + retention-days: 14 diff --git a/BUILD b/BUILD index c3780833..4735211f 100644 --- a/BUILD +++ b/BUILD @@ -51,3 +51,14 @@ exports_files( "MODULE.bazel", ], ) + +test_suite( + name = "component_tests", + tests = [ + "//score/time/high_res_steady_time/src:high_res_steady_clock_test", + "//score/time/steady_time/src:steady_clock_test", + "//score/time/system_time/src:system_clock_test", + "//score/time/vehicle_time/src:vehicle_clock_test", + ], + visibility = ["//visibility:public"], +) diff --git a/docs/index.rst b/docs/index.rst index 4a401bb6..df4c0e3b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -44,6 +44,7 @@ For a detailed concept and architectural design, please refer to the :doc:`time_ features/index module/index + quality_pack Project Layout diff --git a/docs/quality_pack.rst b/docs/quality_pack.rst new file mode 100644 index 00000000..4c8da226 --- /dev/null +++ b/docs/quality_pack.rst @@ -0,0 +1,142 @@ +.. + # ******************************************************************************* + # 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 + # ******************************************************************************* + +Quality Pack Targets +#################### + +The ``score_time`` module plugs into the Score docs-as-code +dashboards and quality gates as described in the upstream how-to: +https://eclipse-score.github.io/docs-as-code/main/how-to/dashboards_and_quality_gates.html. + +The Bazel targets below are the ones consumed by CI to produce +dashboard artefacts and to enforce traceability thresholds. + +Unit tests +========== + +- **Tag:** ``unit`` (already carried by every ``cc_test`` under + ``//score/...``). +- **Command:** ``bazel test --config=time-x86_64-linux //score/...`` — this + runs the full unit-test set because every ``cc_test`` in the tree + carries the ``unit`` tag. +- **Results:** JUnit XML and stdout log per test target under + ``bazel-testlogs///{test.log,test.xml}``. + +Component tests +=============== + +Component tests exercise a clock facade (``Clock``) together with a +mocked backend via ``ScopedClockOverride`` — the seam between the framework +layer and a domain-specific backend is covered end to end. + +- **Tag:** ``component``. +- **Aggregate target:** ``//:component_tests``. +- **Command:** ``bazel test --config=time-x86_64-linux //:component_tests``. +- **Included tests (existing tests reclassified, not new ones):** + + - ``//score/time/vehicle_time/src:vehicle_clock_test`` + - ``//score/time/high_res_steady_time/src:high_res_steady_clock_test`` + - ``//score/time/system_time/src:system_clock_test`` + - ``//score/time/steady_time/src:steady_clock_test`` + +- **Results:** JUnit XML and stdout log per test target under + ``bazel-testlogs///{test.log,test.xml}``. + +Code coverage +============= + +- **Command:** ``.github/tools/coverage.sh //score/... --config time-x86_64-linux``. +- **Underlying target:** ``bazel coverage`` with the ``coverage`` config + from ``.bazelrc``. +- **Results:** HTML report at ``cpp_coverage/index.html`` and Cobertura + XML at ``cpp_coverage/coverage.xml``. The raw ``lcov`` data lives under + ``$(bazel info output_path)/_coverage/_coverage_report.dat``. +- **CI:** ``.github/workflows/code-coverage.yml`` runs the reusable + ``eclipse-score/cicd-workflows`` coverage workflow with the same + target and config and enforces the configured minimum coverage + threshold. + +Requirements traceability (dashboards + gate) +============================================= + +Component requirements live alongside the module under +``score/time/docs/requirements/requirements.rst`` and use the Score +metamodel ``comp_req::`` directive. Feature-level requirements +(``feat_req::``) belong to the upstream ``eclipse-score/score`` repo and +are consumed here via the external needs.json feed. Source-code and +test-code links are consumed by ``score_docs_as_code``: + +- **Source-code markers** — in the C++ implementation: + + .. code-block:: cpp + + // # req-Id: comp_req__vehicle_time__snapshot + Snapshot Now() { ... } + + The leading ``// #`` is intentional; the linker regex looks for the + literal token ``# req-Id:`` and this is the neutral C++ form. The + files that carry markers are collected in + ``//score/time/vehicle_time/src:requirement_marked_sources`` (a + ``filegroup``) and passed to the root ``docs()`` macro via its + ``scan_code`` attribute. + +- **Test-code links** — use GoogleTest ``RecordProperty`` inside each + linked test body: + + .. code-block:: cpp + + TEST(VehicleClockTest, InitForwardsToBackend) + { + ::testing::Test::RecordProperty("FullyVerifies", "comp_req__vehicle_time__lifecycle"); + ::testing::Test::RecordProperty("TestType", "requirements-based"); + ::testing::Test::RecordProperty("DerivationTechnique", "requirements-analysis"); + ::testing::Test::RecordProperty("Description", "…"); + ... + } + + The properties land in ``bazel-testlogs/.../test.xml`` and are read by + ``score_source_code_linker`` when docs are built. + +- **Bazel targets:** + + - ``//:needs_json`` — needs.json produced by Sphinx-Needs. + - ``//:metrics_json`` — traceability metrics extracted from needs.json. + - ``//:traceability_gate`` — enforces coverage thresholds. + +- **Local flow** (order matters — the gate reads ``bazel-testlogs`` for + test links): + + .. code-block:: bash + + bazel test --config=time-x86_64-linux //:component_tests //score/... + bazel run //:docs + bazel run //:traceability_gate -- \ + --metrics-json "$(pwd)/_build/metrics.json" \ + --need-type comp_req \ + --min-req-code 100 \ + --min-req-test 62 \ + --min-req-fully-linked 62 \ + --min-tests-linked 1 + + The thresholds above are the enforced contract. Live metric values + are produced by ``//:metrics_json`` on every build; consult that + artefact (or the CI dashboard) for current numbers. + +.. note:: + + The exact target names and result folders above are the current + convention for this repository. They can be renamed together with + ``@Zwinkau Andreas (ETAS-ECM ESY3)`` if a project-wide naming scheme + is agreed upon; the CI workflows in ``.github/workflows`` reference + these targets directly and would need to move in lockstep. diff --git a/score/time/BUILD b/score/time/BUILD index 9e9403f2..255c1aba 100644 --- a/score/time/BUILD +++ b/score/time/BUILD @@ -16,6 +16,13 @@ load("@score_docs_as_code//:docs.bzl", "docs_bundle") docs_bundle( name = "docs_bundle", + code_targets = [ + "//score/time/clock/src:requirement_marked_sources", + "//score/time/high_res_steady_time/src:requirement_marked_sources", + "//score/time/steady_time/src:requirement_marked_sources", + "//score/time/system_time/src:requirement_marked_sources", + "//score/time/vehicle_time/src:requirement_marked_sources", + ], source_dir = "docs", visibility = ["//visibility:public"], ) diff --git a/score/time/clock/src/BUILD b/score/time/clock/src/BUILD index ebf6e914..95980ed6 100644 --- a/score/time/clock/src/BUILD +++ b/score/time/clock/src/BUILD @@ -14,6 +14,14 @@ load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +# Source files carrying `// # req-Id:` markers, exposed to +# //score/time:docs_bundle via its code_targets attribute. +filegroup( + name = "requirement_marked_sources", + srcs = ["clock.h"], + visibility = ["//visibility:public"], +) + cc_library( name = "clock_core", hdrs = [ diff --git a/score/time/clock/src/clock.h b/score/time/clock/src/clock.h index 45bbe896..ce40c6a4 100644 --- a/score/time/clock/src/clock.h +++ b/score/time/clock/src/clock.h @@ -93,6 +93,7 @@ class Clock /// /// Acquires @c instance_guard_ mutex to protect static state. Returns override if active, /// cached instance if alive, or creates fresh backend via @c detail::CreateBackend(). + // # req-Id: comp_req__time__unified_clock_facade [[nodiscard]] static Clock GetInstance() noexcept { std::lock_guard lock{instance_guard_}; @@ -112,6 +113,7 @@ class Clock /// @brief Returns the current clock snapshot (time_point + optional status). /// /// Delegates to backend via @c Trait::CallNow(). Synchronous; thread safety depends on backend. + // # req-Id: comp_req__time__snapshot_with_status [[nodiscard]] Snapshot Now() const noexcept { return Trait::CallNow(*impl_); @@ -149,6 +151,7 @@ class Clock /// Calling this on an always-ready clock (HighResSteadyTime, steady_clock) is a compile error. /// /// @return @c true if the backend is ready; @c false on failure. + // # req-Id: comp_req__time__explicit_lifecycle template ::value, bool> = true> [[nodiscard]] bool Init() noexcept { @@ -159,6 +162,7 @@ class Clock /// /// Only available for clock domains that require a readiness check (e.g. VehicleTime). /// Calling this on an always-available clock (HighResSteadyTime, steady_clock) is a compile error. + // # req-Id: comp_req__time__explicit_lifecycle template ::value, int> = 0> [[nodiscard]] bool IsAvailable() const noexcept { @@ -174,6 +178,7 @@ class Clock /// @param until Steady-clock deadline after which the wait is abandoned. /// /// @return @c true if the resource became available before the deadline. + // # req-Id: comp_req__time__explicit_lifecycle template ::value, int> = 0> [[nodiscard]] bool WaitUntilAvailable(const score::cpp::stop_token& token, std::chrono::steady_clock::time_point until) const noexcept diff --git a/score/time/docs/requirements/chklst_req_inspection.rst b/score/time/docs/requirements/chklst_req_inspection.rst index 8f0d944c..de66b303 100644 --- a/score/time/docs/requirements/chklst_req_inspection.rst +++ b/score/time/docs/requirements/chklst_req_inspection.rst @@ -190,5 +190,3 @@ And also the following AoUs in "valid" state and with "inspected" tag set (for t :colwidths: 25,25,25 :sort: title -.. attention:: - The above tables filtering must be updated according to your Component. diff --git a/score/time/docs/requirements/index.rst b/score/time/docs/requirements/index.rst index 400d1d03..c46dc9c6 100644 --- a/score/time/docs/requirements/index.rst +++ b/score/time/docs/requirements/index.rst @@ -12,13 +12,172 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -Requirements -############ +Time Component Requirements +########################### -.. note:: - Work in progress: page structure only, content to follow in later PRs. +.. document:: Time Requirements + :id: doc__time_requirements + :status: valid + :version: 1 + :safety: ASIL_B + :security: NO + :realizes: wp__requirements_comp[version==1] + :tags: requirements, time + +Functional Requirements +----------------------- + +.. comp_req:: Unified clock facade across time domains + :id: comp_req__time__unified_clock_facade + :reqtype: Functional + :security: NO + :safety: ASIL_B + :derived_from: feat_req__time__high_prec_clock_api, feat_req__time__monotonic_clock_api, feat_req__time__abs_base_api, feat_req__time__vehicle_time_time_api + :status: valid + :version: 1 + :satisfied_by: comp__time + + The Component shall provide a single type-safe accessor for all supported + clock domains that prevents mixing time values from different domains by + rejecting domain-mismatched operations at compile time when accessing + time snapshots. + +.. comp_req:: Immutable snapshot with quality metadata + :id: comp_req__time__snapshot_with_status + :reqtype: Functional + :security: NO + :safety: ASIL_B + :derived_from: feat_req__time__vehicle_time_time_api + :status: valid + :version: 1 + :satisfied_by: comp__time + + The Component shall return a single immutable value that bundles + the timepoint with the domain's synchronization and quality + metadata, so callers observe state that cannot change after + creation and can determine time validity without a separate + status query. + +.. comp_req:: Explicit lifecycle for backends that need it + :id: comp_req__time__explicit_lifecycle + :reqtype: Functional + :security: NO + :safety: ASIL_B + :derived_from: feat_req__time__vehicle_time_ctrl_flow + :status: valid + :version: 1 + :satisfied_by: comp__time + + The Component shall provide initialization, availability-check, and + availability-wait operations for clock domains that depend on external + resources, and shall cause compilation failure when those operations + are invoked on clock domains that are always ready. + +.. comp_req:: VehicleClock returns snapshot with status + :id: comp_req__vehicle_time__snapshot + :reqtype: Functional + :security: NO + :safety: ASIL_B + :derived_from: feat_req__time__vehicle_time_time_api + :status: valid + :version: 1 + :satisfied_by: comp__time + + The Component shall read the vehicle time timepoint and its status + atomically from the backend within a single backend operation, so + downstream callers observe consistent time and status values. + +.. comp_req:: VehicleClock lifecycle operations + :id: comp_req__vehicle_time__lifecycle + :reqtype: Functional + :security: NO + :safety: ASIL_B + :derived_from: feat_req__time__vehicle_time_ctrl_flow + :status: valid + :version: 1 + :satisfied_by: comp__time + + The Component shall delegate initialization and + availability checks to the backend and shall report backend + init failure and availability-wait timeouts to the caller without + blocking indefinitely. + +.. comp_req:: HighResSteadyClock always-ready snapshot + :id: comp_req__high_res_steady_time__snapshot + :reqtype: Functional + :security: NO + :safety: ASIL_B + :derived_from: feat_req__time__high_prec_clock_api + :status: valid + :version: 1 + :satisfied_by: comp__time + + The Component shall provide steady high-resolution time snapshots + without requiring prior initialization. + +.. comp_req:: SteadyClock always-ready snapshot + :id: comp_req__steady_time__snapshot + :reqtype: Functional + :security: NO + :safety: ASIL_B + :derived_from: feat_req__time__monotonic_clock_api + :status: valid + :version: 1 + :satisfied_by: comp__time + + The Component shall provide steady time snapshots without + requiring prior initialization, and shall cause compilation failure + when initialization or availability operations are invoked on the + steady time domain. + +.. comp_req:: SystemClock always-ready snapshot + :id: comp_req__system_time__snapshot + :reqtype: Functional + :security: NO + :safety: ASIL_B + :derived_from: feat_req__time__abs_base_api + :status: valid + :version: 1 + :satisfied_by: comp__time + + The Component shall provide system time snapshots without + requiring prior initialization, and shall cause compilation failure + when initialization or availability operations are invoked on the + system time domain. + +.. comp_req:: Supported platforms + :id: comp_req__time__supported_platforms + :reqtype: Non-Functional + :security: NO + :safety: ASIL_B + :status: valid + :version: 1 + :satisfied_by: comp__time + + The Component shall build and run on Linux and QNX host platforms, + providing platform-specific backends where the underlying OS APIs + differ. + +Assumption of Use Requirements +------------------------------ + +.. aou_req:: Backend initialization by the user + :id: aou_req__time__user_initializes_backend + :reqtype: Process + :security: NO + :safety: ASIL_B + :status: valid + :version: 1 + + The Component User shall initialize the backends for clock domains + that require initialization before requesting time snapshots + from those domains. + + +.. needextend:: "c.this_doc()" + :+tags: time .. toctree:: + :maxdepth: 1 - requirements chklst_req_inspection diff --git a/score/time/docs/requirements/requirements.rst b/score/time/docs/requirements/requirements.rst deleted file mode 100644 index 10945b8d..00000000 --- a/score/time/docs/requirements/requirements.rst +++ /dev/null @@ -1,107 +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 Time Requirements -############################ - -.. document:: Time Requirements - :id: doc__time_requirements - :status: draft - :version: 1 - :safety: ASIL_B - :security: NO - :realizes: wp__requirements_comp[version==1] - -.. note:: - Work in progress: structure, titles, and needs IDs only. Content and req/comp/feat traceability links to follow in later PRs. - -.. attention:: - The above directive must be updated according to your Component. - - - Adjust ``status`` to be ``valid`` - - Adjust ``safety``, ``security`` and ``tags`` according to your needs - - -=================================================================== - -Functional Requirements ------------------------ - -.. code-block:: rst - - .. comp_req:: Some Title - :id: comp_req__time__some_title - :reqtype: Functional - :security: NO - :safety: ASIL_B - :derived_from: feat_req__time__example_req - :status: invalid - :version: 1 - :satisfied_by: comp__time - - The Component shall do xyz to another component to bring it to this condition at this time - - Note: (optional, not to be verified) - -.. attention:: - The above directive must be updated according to your component requirements. - - - Replace the example content by the real content for your first requirement - - Set ``derived_from`` with links to Feature requirements - - Set ``satisfied_by`` with a link to the right Component id - - Set ``safety`` and ``security`` to the right value - - Set the status to valid and start the review/merge process - - Add other needed requirements for your component - -Assumption of Use Requirements ------------------------------- - -.. aou_req:: Next Title - :id: aou_req__time__next_title - :reqtype: Process - :security: NO - :safety: ASIL_B - :status: invalid - :version: 1 - - The Component User shall do xyz to use the component safely/securely - -Environmental Requirements --------------------------- - -.. aou_req:: Another Title - :id: aou_req__time__another - :reqtype: Process - :security: NO - :safety: ASIL_B - :status: invalid - :version: 1 - :tags: environment - - The Component shall only be used in a xyz environment to ensure its proper functioning. - -Hints ------ - -.. attention:: - The above directives must be updated according to your feature requirements. - - - Replace the example content by the real content for your first requirement (according to :need:`gd_guidl__req_engineering`) - - Set ``safety`` and ``security`` to the right value (ASIL B/QM; YES/NO) - - Set ``reqtype`` with a link to the right value () - - Add other needed requirements for your feature - - Set ``status`` to ``valid`` and start the review/merge process - -.. needextend:: "c.this_doc()" - :+tags: time diff --git a/score/time/high_res_steady_time/src/BUILD b/score/time/high_res_steady_time/src/BUILD index 6c796059..789fb0aa 100644 --- a/score/time/high_res_steady_time/src/BUILD +++ b/score/time/high_res_steady_time/src/BUILD @@ -14,6 +14,14 @@ load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +# Source files carrying `// # req-Id:` markers, exposed to +# //score/time:docs_bundle via its code_targets attribute. +filegroup( + name = "requirement_marked_sources", + srcs = ["high_res_steady_clock.cpp"], + visibility = ["//visibility:public"], +) + cc_library( name = "high_res_steady_clock", srcs = ["high_res_steady_clock.cpp"], diff --git a/score/time/high_res_steady_time/src/high_res_steady_clock.cpp b/score/time/high_res_steady_time/src/high_res_steady_clock.cpp index c1945450..b39c7972 100644 --- a/score/time/high_res_steady_time/src/high_res_steady_clock.cpp +++ b/score/time/high_res_steady_time/src/high_res_steady_clock.cpp @@ -18,6 +18,7 @@ namespace score namespace time { +// # req-Id: comp_req__high_res_steady_time__snapshot ClockTraits::Snapshot ClockTraits::CallNow(const Backend& impl) noexcept { return impl.Now(); diff --git a/score/time/high_res_steady_time/src/high_res_steady_clock_adapter_test.cpp b/score/time/high_res_steady_time/src/high_res_steady_clock_adapter_test.cpp index 2ad6ce90..fee7c8d6 100644 --- a/score/time/high_res_steady_time/src/high_res_steady_clock_adapter_test.cpp +++ b/score/time/high_res_steady_time/src/high_res_steady_clock_adapter_test.cpp @@ -28,6 +28,13 @@ namespace time TEST(HighResSteadyClockTest, NowReturnsTimepointSuitableForDurationArithmetic) { + ::testing::Test::RecordProperty("PartiallyVerifies", "comp_req__high_res_steady_time__snapshot"); + ::testing::Test::RecordProperty("TestType", "requirements-based"); + ::testing::Test::RecordProperty("DerivationTechnique", "requirements-analysis"); + ::testing::Test::RecordProperty( + "Description", + "HighResSteadyClock::Now returns a timepoint suitable for duration arithmetic without requiring Init."); + auto mock = std::make_shared(); test_utils::ScopedClockOverride guard{mock}; diff --git a/score/time/steady_time/src/BUILD b/score/time/steady_time/src/BUILD index bc817931..7ac8aab4 100644 --- a/score/time/steady_time/src/BUILD +++ b/score/time/steady_time/src/BUILD @@ -14,6 +14,14 @@ load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +# Source files carrying `// # req-Id:` markers, exposed to +# //score/time:docs_bundle via its code_targets attribute. +filegroup( + name = "requirement_marked_sources", + srcs = ["steady_clock.cpp"], + visibility = ["//visibility:public"], +) + cc_library( name = "steady_clock", srcs = ["steady_clock.cpp"], diff --git a/score/time/steady_time/src/steady_clock.cpp b/score/time/steady_time/src/steady_clock.cpp index c4cc4490..c91822cb 100644 --- a/score/time/steady_time/src/steady_clock.cpp +++ b/score/time/steady_time/src/steady_clock.cpp @@ -18,6 +18,7 @@ namespace score namespace time { +// # req-Id: comp_req__steady_time__snapshot ClockTraits::Snapshot ClockTraits::CallNow( const Backend& impl) noexcept { diff --git a/score/time/steady_time/src/steady_clock_adapter_test.cpp b/score/time/steady_time/src/steady_clock_adapter_test.cpp index 39e22c32..eb07b1e7 100644 --- a/score/time/steady_time/src/steady_clock_adapter_test.cpp +++ b/score/time/steady_time/src/steady_clock_adapter_test.cpp @@ -44,6 +44,12 @@ class SampleSteadyService TEST(SteadyClockTest, NowReturnsTimepointSuitableForDurationArithmetic) { + ::testing::Test::RecordProperty("PartiallyVerifies", "comp_req__steady_time__snapshot"); + ::testing::Test::RecordProperty("TestType", "requirements-based"); + ::testing::Test::RecordProperty("DerivationTechnique", "requirements-analysis"); + ::testing::Test::RecordProperty("Description", + "SteadyClock::Now returns a snapshot backed by std::chrono::steady_clock."); + auto mock = std::make_shared(); test_utils::ScopedClockOverride guard{mock}; diff --git a/score/time/system_time/src/BUILD b/score/time/system_time/src/BUILD index 3917cbe7..a470fb85 100644 --- a/score/time/system_time/src/BUILD +++ b/score/time/system_time/src/BUILD @@ -14,6 +14,14 @@ load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +# Source files carrying `// # req-Id:` markers, exposed to +# //score/time:docs_bundle via its code_targets attribute. +filegroup( + name = "requirement_marked_sources", + srcs = ["system_clock.cpp"], + visibility = ["//visibility:public"], +) + cc_library( name = "system_clock", srcs = ["system_clock.cpp"], diff --git a/score/time/system_time/src/system_clock.cpp b/score/time/system_time/src/system_clock.cpp index 26f7901d..02a3b957 100644 --- a/score/time/system_time/src/system_clock.cpp +++ b/score/time/system_time/src/system_clock.cpp @@ -18,6 +18,7 @@ namespace score namespace time { +// # req-Id: comp_req__system_time__snapshot ClockTraits::Snapshot ClockTraits::CallNow( const Backend& impl) noexcept { diff --git a/score/time/system_time/src/system_clock_adapter_test.cpp b/score/time/system_time/src/system_clock_adapter_test.cpp index b000e645..dac2a537 100644 --- a/score/time/system_time/src/system_clock_adapter_test.cpp +++ b/score/time/system_time/src/system_clock_adapter_test.cpp @@ -44,6 +44,12 @@ class SampleSystemService TEST(SystemClockTest, NowReturnsTimepointSuitableForDurationArithmetic) { + ::testing::Test::RecordProperty("PartiallyVerifies", "comp_req__system_time__snapshot"); + ::testing::Test::RecordProperty("TestType", "requirements-based"); + ::testing::Test::RecordProperty("DerivationTechnique", "requirements-analysis"); + ::testing::Test::RecordProperty("Description", + "SystemClock::Now returns a snapshot backed by std::chrono::system_clock."); + auto mock = std::make_shared(); test_utils::ScopedClockOverride guard{mock}; diff --git a/score/time/vehicle_time/src/BUILD b/score/time/vehicle_time/src/BUILD index 49ae57ef..e5459e3a 100644 --- a/score/time/vehicle_time/src/BUILD +++ b/score/time/vehicle_time/src/BUILD @@ -14,6 +14,14 @@ load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +# Source files carrying `// # req-Id:` markers, exposed to the root +# docs() macro's scan_code attribute for source-code traceability. +filegroup( + name = "requirement_marked_sources", + srcs = ["vehicle_clock.cpp"], + visibility = ["//visibility:public"], +) + cc_library( name = "vehicle_clock", srcs = ["vehicle_clock.cpp"], diff --git a/score/time/vehicle_time/src/vehicle_clock.cpp b/score/time/vehicle_time/src/vehicle_clock.cpp index ee6af884..ef441677 100644 --- a/score/time/vehicle_time/src/vehicle_clock.cpp +++ b/score/time/vehicle_time/src/vehicle_clock.cpp @@ -41,21 +41,25 @@ std::ostringstream ClockStatus::PrintTo() const return oss; } +// # req-Id: comp_req__vehicle_time__snapshot ClockTraits::Snapshot ClockTraits::CallNow(const Backend& impl) noexcept { return impl.Now(); } +// # req-Id: comp_req__vehicle_time__lifecycle bool InitializationHook::CallInit(Backend& impl) noexcept { return impl.Init(); } +// # req-Id: comp_req__vehicle_time__lifecycle bool AvailabilityHook::CallIsAvailable(const Backend& impl) noexcept { return impl.IsAvailable(); } +// # req-Id: comp_req__vehicle_time__lifecycle bool AvailabilityHook::CallWaitUntilAvailable(const Backend& impl, const score::cpp::stop_token& token, std::chrono::steady_clock::time_point until) noexcept diff --git a/score/time/vehicle_time/src/vehicle_clock_test.cpp b/score/time/vehicle_time/src/vehicle_clock_test.cpp index 06bc9b00..f160f743 100644 --- a/score/time/vehicle_time/src/vehicle_clock_test.cpp +++ b/score/time/vehicle_time/src/vehicle_clock_test.cpp @@ -52,6 +52,12 @@ class SampleVehicleService TEST(VehicleClockTest, NowReturnsSynchronizedStatusAndTimepoint) { + ::testing::Test::RecordProperty("PartiallyVerifies", "comp_req__vehicle_time__snapshot"); + ::testing::Test::RecordProperty("TestType", "requirements-based"); + ::testing::Test::RecordProperty("DerivationTechnique", "equivalence-classes"); + ::testing::Test::RecordProperty("Description", + "VehicleClock::Now returns a snapshot combining backend timepoint and status."); + auto mock = std::make_shared(); test_utils::ScopedClockOverride guard{mock}; @@ -97,6 +103,11 @@ TEST(VehicleClockTest, NowIsReliableReturnsFalseWhenTimeoutSet) TEST(VehicleClockTest, InitForwardsToBackend) { + ::testing::Test::RecordProperty("PartiallyVerifies", "comp_req__vehicle_time__lifecycle"); + ::testing::Test::RecordProperty("TestType", "requirements-based"); + ::testing::Test::RecordProperty("DerivationTechnique", "requirements-analysis"); + ::testing::Test::RecordProperty("Description", "VehicleClock::Init delegates to the backend Init call."); + auto mock = std::make_shared(); test_utils::ScopedClockOverride guard{mock}; @@ -107,6 +118,11 @@ TEST(VehicleClockTest, InitForwardsToBackend) TEST(VehicleClockTest, IsAvailableReturnsTrueWhenBackendReports) { + ::testing::Test::RecordProperty("PartiallyVerifies", "comp_req__vehicle_time__lifecycle"); + ::testing::Test::RecordProperty("TestType", "requirements-based"); + ::testing::Test::RecordProperty("DerivationTechnique", "requirements-analysis"); + ::testing::Test::RecordProperty("Description", "VehicleClock::IsAvailable delegates to the backend IsAvailable call."); + auto mock = std::make_shared(); test_utils::ScopedClockOverride guard{mock}; @@ -117,6 +133,11 @@ TEST(VehicleClockTest, IsAvailableReturnsTrueWhenBackendReports) TEST(VehicleClockTest, IsAvailableReturnsFalseWhenBackendUnavailable) { + ::testing::Test::RecordProperty("PartiallyVerifies", "comp_req__vehicle_time__lifecycle"); + ::testing::Test::RecordProperty("TestType", "requirements-based"); + ::testing::Test::RecordProperty("DerivationTechnique", "requirements-analysis"); + ::testing::Test::RecordProperty("Description", "VehicleClock::IsAvailable delegates to the failing backend IsAvailable call."); + auto mock = std::make_shared(); test_utils::ScopedClockOverride guard{mock}; @@ -127,6 +148,11 @@ TEST(VehicleClockTest, IsAvailableReturnsFalseWhenBackendUnavailable) TEST(VehicleClockTest, WaitUntilAvailableForwardsTokenAndDeadlineToBackend) { + ::testing::Test::RecordProperty("PartiallyVerifies", "comp_req__vehicle_time__lifecycle"); + ::testing::Test::RecordProperty("TestType", "requirements-based"); + ::testing::Test::RecordProperty("DerivationTechnique", "requirements-analysis"); + ::testing::Test::RecordProperty("Description", "VehicleClock::WaitUntilAvailable delegates to the backend WaitUntilAvailable call."); + auto mock = std::make_shared(); test_utils::ScopedClockOverride guard{mock};