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/BUILD b/BUILD index b5f6ba4c..53f6a9c5 100644 --- a/BUILD +++ b/BUILD @@ -84,3 +84,24 @@ use_format_targets(languages = [ "yaml", "cpp", ]) + +# Aggregated component-test suite. Component tests exercise a clock facade +# (Clock) together with a mocked backend, i.e. more than one unit of code +# but without a real driver. Run with: +# bazel test --config=time-x86_64-linux //:component_tests +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"], +) + +# Unit tests: every cc_test under //score/... is already tagged "unit", +# so `bazel test //score/...` is the canonical unit-tests invocation. +# No aggregate test_suite is needed here — a `test_suite` in a top-level +# BUILD file cannot use `//score/...` as an element of its `tests` +# attribute (package wildcards are rejected). diff --git a/docs/features/time/feature_requirements.rst b/docs/features/time/feature_requirements.rst new file mode 100644 index 00000000..924d7716 --- /dev/null +++ b/docs/features/time/feature_requirements.rst @@ -0,0 +1,50 @@ +Feature Requirements +==================== + +.. feat_req:: Unified clock facade across time domains + :id: feat_req__time__unified_clock_facade + :reqtype: Interface + :security: NO + :safety: QM + :status: valid + :version: 1 + :valid_from: v1.0 + :satisfied_by: feat__time + + ``score::time`` shall expose a single, type-safe entry point + (``Clock::GetInstance``) for reading time snapshots across the + supported clock domains (``VehicleTime``, ``HighResSteadyTime``, + ``std::chrono::steady_clock``, ``std::chrono::system_clock``), so + clients select a clock domain at compile time and cannot accidentally + mix domains at run time. + +.. feat_req:: Immutable snapshot with quality metadata + :id: feat_req__time__snapshot_with_status + :reqtype: Functional + :security: NO + :safety: QM + :status: valid + :version: 1 + :valid_from: v1.0 + :satisfied_by: feat__time + + Every ``Clock::Now`` call shall return a single immutable + ``ClockSnapshot`` value that bundles the timepoint with the domain's + status metadata, so callers can inspect synchronization quality + without a separate status call. + +.. feat_req:: Explicit lifecycle for backends that need it + :id: feat_req__time__explicit_lifecycle + :reqtype: Functional + :security: NO + :safety: QM + :status: valid + :version: 1 + :valid_from: v1.0 + :satisfied_by: feat__time + + Clock domains that depend on an external resource (currently + ``VehicleTime``) shall provide ``Init``, ``IsAvailable`` and + ``WaitUntilAvailable`` operations, and shall keep those operations + unavailable — at compile time — on clock domains that are always + ready. diff --git a/docs/features/time/index.rst b/docs/features/time/index.rst index 653b8a15..da85e914 100644 --- a/docs/features/time/index.rst +++ b/docs/features/time/index.rst @@ -5,6 +5,12 @@ score::time — Unified Clock Interface :depth: 3 :local: +.. toctree:: + :maxdepth: 1 + :caption: Requirements + + feature_requirements + Overview -------- diff --git a/docs/index.rst b/docs/index.rst index bf37b218..f0e2055b 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..a8d1ce77 --- /dev/null +++ b/docs/quality_pack.rst @@ -0,0 +1,136 @@ +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) +============================================= + +Feature requirements live under ``docs/features/time/feature_requirements.rst``. +Component requirements live alongside each component under +``score/time//docs/requirements/requirements.rst`` +(``vehicle_time``, ``steady_time``, ``system_time``, ``high_res_steady_time``), +matching the ``module_template`` layout. All entries use the Score metamodel +directives (``feat_req::`` / ``comp_req::``). 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 40 \ + --min-req-test 100 \ + --min-req-fully-linked 40 \ + --min-tests-linked 15 + + Current baseline (component requirements only): + + ========================= =============== + Metric Value + ========================= =============== + Requirements with source 2/5 (40.0%) + Requirements with test 5/5 (100.0%) + Requirements fully linked 2/5 (40.0%) + Tests linked to reqs 5/27 (18.5%) + ========================= =============== + +.. 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 7f707a4b..e4f02d1d 100644 --- a/score/time/BUILD +++ b/score/time/BUILD @@ -17,6 +17,9 @@ load("@score_docs_as_code//:docs.bzl", "docs_bundle") docs_bundle( name = "docs_bundle", + code_targets = [ + "//score/time/vehicle_time/src:requirement_marked_sources", + ], source_dir = "docs", visibility = ["//visibility:public"], ) diff --git a/score/time/docs/requirements/requirements.rst b/score/time/docs/requirements/requirements.rst index 6589d6db..8b284b95 100644 --- a/score/time/docs/requirements/requirements.rst +++ b/score/time/docs/requirements/requirements.rst @@ -13,96 +13,89 @@ # ******************************************************************************* Component Time Requirements -############################ +########################### .. document:: Time Requirements :id: doc__time_requirements - :status: draft + :status: valid :version: 1 - :safety: ASIL_B + :safety: QM :security: NO :realizes: wp__requirements_comp[version==1] - :tags: time - -.. 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 - - -=================================================================== + :tags: requirements, time 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) +.. comp_req:: VehicleClock returns snapshot with status + :id: comp_req__vehicle_time__snapshot + :reqtype: Functional + :security: NO + :safety: QM + :derived_from: feat_req__time__snapshot_with_status + :status: valid + :version: 1 + :satisfied_by: comp__time -.. attention:: - The above directive must be updated according to your component requirements. + ``VehicleClock::Now`` shall return a ``ClockSnapshot`` whose timepoint + and ``VehicleTimeStatus`` originate from the same backend read, so + downstream callers observe consistent time and status values. - - 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 +.. comp_req:: VehicleClock lifecycle operations + :id: comp_req__vehicle_time__lifecycle + :reqtype: Functional + :security: NO + :safety: QM + :derived_from: feat_req__time__explicit_lifecycle + :status: valid + :version: 1 + :satisfied_by: comp__time -Assumption of Use Requirements ------------------------------- + ``VehicleClock`` shall provide ``Init``, ``IsAvailable`` and + ``WaitUntilAvailable`` operations that delegate to the backend and + report backend init failure and availability-wait timeouts to the + caller without blocking indefinitely. -.. aou_req:: Next Title - :id: aou_req__time__next_title - :reqtype: Process +.. comp_req:: HighResSteadyClock always-ready snapshot + :id: comp_req__high_res_steady_time__snapshot + :reqtype: Functional :security: NO - :safety: ASIL_B - :status: invalid + :safety: QM + :derived_from: feat_req__time__unified_clock_facade + :status: valid :version: 1 + :satisfied_by: comp__time - The Component User shall do xyz to use the component safely/securely - -Environmental Requirements --------------------------- + ``HighResSteadyClock::Now`` shall return a monotonic + ``ClockSnapshot`` without requiring prior initialization, and shall + not expose ``Init`` / ``IsAvailable`` / ``WaitUntilAvailable`` on the + facade (using them is a compile error). -.. aou_req:: Another Title - :id: aou_req__time__another - :reqtype: Process +.. comp_req:: SteadyClock always-ready snapshot + :id: comp_req__steady_time__snapshot + :reqtype: Functional :security: NO - :safety: ASIL_B - :status: invalid + :safety: QM + :derived_from: feat_req__time__unified_clock_facade + :status: valid :version: 1 - :tags: environment + :satisfied_by: comp__time - The Component shall only be used in a xyz environment to ensure its proper functioning. + ``SteadyClock::Now`` shall return a snapshot backed by + ``std::chrono::steady_clock`` without requiring initialization. -Hints ------ - -.. attention:: - The above directives must be updated according to your feature requirements. +.. comp_req:: SystemClock always-ready snapshot + :id: comp_req__system_time__snapshot + :reqtype: Functional + :security: NO + :safety: QM + :derived_from: feat_req__time__unified_clock_facade + :status: valid + :version: 1 + :satisfied_by: comp__time - - 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 + ``SystemClock::Now`` shall return a snapshot backed by + ``std::chrono::system_clock`` without requiring initialization. .. needextend:: is_external == False and "time" in id :+tags: time diff --git a/score/time/high_res_steady_time/src/BUILD b/score/time/high_res_steady_time/src/BUILD index cc891d3a..5a4a84e6 100644 --- a/score/time/high_res_steady_time/src/BUILD +++ b/score/time/high_res_steady_time/src/BUILD @@ -75,6 +75,7 @@ cc_test( srcs = ["high_res_steady_clock_adapter_test.cpp"], features = COMPILER_WARNING_FEATURES, tags = [ + "component", "exclusive", "unit", ], 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..7f403421 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,12 @@ namespace time TEST(HighResSteadyClockTest, NowReturnsTimepointSuitableForDurationArithmetic) { + ::testing::Test::RecordProperty("FullyVerifies", "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 monotonic snapshot 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..d8605259 100644 --- a/score/time/steady_time/src/BUILD +++ b/score/time/steady_time/src/BUILD @@ -60,6 +60,7 @@ cc_test( srcs = ["steady_clock_adapter_test.cpp"], features = COMPILER_WARNING_FEATURES, tags = [ + "component", "exclusive", "unit", ], 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..50f15955 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("FullyVerifies", "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..8c3787f1 100644 --- a/score/time/system_time/src/BUILD +++ b/score/time/system_time/src/BUILD @@ -60,6 +60,7 @@ cc_test( srcs = ["system_clock_adapter_test.cpp"], features = COMPILER_WARNING_FEATURES, tags = [ + "component", "exclusive", "unit", ], 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..d0260e45 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("FullyVerifies", "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..6d9e59ff 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"], @@ -65,6 +73,7 @@ cc_test( srcs = ["vehicle_clock_test.cpp"], features = COMPILER_WARNING_FEATURES, tags = [ + "component", "exclusive", "unit", ], 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..91b9804e 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("FullyVerifies", "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("FullyVerifies", "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};