Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions .github/tools/coverage.sh
Original file line number Diff line number Diff line change
@@ -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 [<bazel-target>] [--config <bazel-config>] [--output-dir <dir>]
#
# Options:
# <bazel-target> Bazel target to collect coverage for (default: //score/...)
# --config <bazel-config> Bazel config to use (default: time-x86_64-linux)
# --output-dir <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"
24 changes: 24 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ docs(
data = [
"@score_process//:needs_json",
],
scan_code = [
"//score/time/vehicle_time/src:requirement_marked_sources",
],
source_dir = "docs",
)

Expand Down Expand Up @@ -58,3 +61,24 @@ use_format_targets(languages = [
"yaml",
"cpp",
])

# Aggregated component-test suite. Component tests exercise a clock facade
# (Clock<T>) 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).
44 changes: 44 additions & 0 deletions docs/features/time/feature_requirements.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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
:satisfies:

``score::time`` shall expose a single, type-safe entry point
(``Clock<Tag>::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
:satisfies:

Every ``Clock<Tag>::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
:satisfies:

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.
6 changes: 6 additions & 0 deletions docs/features/time/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ score::time — Unified Clock Interface
:depth: 3
:local:

.. toctree::
:maxdepth: 1
:caption: Requirements

feature_requirements

Overview
--------

Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ For a detailed concept and architectural design, please refer to the :doc:`time_
:caption: Contents:

features/index
quality_pack

Project Layout
--------------
Expand Down
136 changes: 136 additions & 0 deletions docs/quality_pack.rst
Original file line number Diff line number Diff line change
@@ -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/<package>/<test>/{test.log,test.xml}``.

Component tests
===============

Component tests exercise a clock facade (``Clock<Tag>``) 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/<package>/<test>/{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/<component>/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.
Loading
Loading