From f71d0e0109c8315d39d2b829ad0170d2f4f38137 Mon Sep 17 00:00:00 2001 From: Anton Krivoborodov Date: Thu, 1 Oct 2026 09:03:25 +0000 Subject: [PATCH 1/6] test(baselibs): add feature integration tests covering baselibs libraries Add baselibs feature integration tests that verify the Eclipse S-CORE base libraries end-to-end on the deployed target image and reference the feat_req__baselibs__* feature requirements they cover, so the coverage shows up in the platform verification report. - Add an on-target demo binary (feature_integration_tests/ baselibs_integration_tests/demo) exposing one subcommand per baselibs feature library (json, utils/base64, bitmanipulation, hash/crc32, containers, result), each printing a deterministic line. - Add a py_itf_test suite (:bit) that drives the demo on the target and asserts the output, annotated via @add_test_properties with the feat_req__baselibs__* needs. The attribute_plugin records PartiallyVerifies into the JUnit XML, which docs-as-code turns into testlink attributes. - Bundle the demo binary into //images/linux_x86_64:image. - Run the suite in the test_and_docs job before the documentation build so the source code linker picks up the test results from bazel-testlogs, mirroring the platform integration tests wiring. --- .github/workflows/test_and_docs.yml | 14 ++ .../baselibs_integration_tests/BUILD | 91 ++++++++ .../baselibs_integration_tests/demo/BUILD | 33 +++ .../baselibs_integration_tests/demo/main.cpp | 218 ++++++++++++++++++ .../baselibs_integration_tests/demo_runner.py | 32 +++ .../test_baselibs_features.py | 106 +++++++++ images/linux_x86_64/BUILD | 9 + 7 files changed, 503 insertions(+) create mode 100644 feature_integration_tests/baselibs_integration_tests/BUILD create mode 100644 feature_integration_tests/baselibs_integration_tests/demo/BUILD create mode 100644 feature_integration_tests/baselibs_integration_tests/demo/main.cpp create mode 100644 feature_integration_tests/baselibs_integration_tests/demo_runner.py create mode 100644 feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/test_and_docs.yml index ea7d1f21c5c..ba8684a4206 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -52,6 +52,11 @@ jobs: - name: Checkout repository if: ${{ github.event_name != 'pull_request_target' }} uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Set up Docker Buildx + # Required by the baselibs integration tests below, which deploy the + # baselibs feature demo via a Docker target built from + # //images/linux_x86_64:image. + uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 - name: Setup Bazel uses: bazel-contrib/setup-bazel@0.18.0 with: @@ -99,6 +104,15 @@ jobs: - name: Execute Feature Integration Tests run: | bazel test --config=linux-x86_64 //feature_integration_tests/test_cases:fit + - name: Execute Baselibs Integration Tests + # Runs in this job - and before the documentation build below - on purpose: + # the source code linker of the documentation build scans `bazel-testlogs` + # of this very workspace. Only tests executed here contribute their + # PartiallyVerifies/FullyVerifies properties as `testlink` attributes to + # the baselibs feature requirement needs, and therefore to the test + # coverage figures of the platform verification report. + run: | + bazel test --lockfile_mode=error --config=linux-x86_64 //feature_integration_tests/baselibs_integration_tests:bit - name: Install uv (required for SBOM license scanning) uses: astral-sh/setup-uv@v5 with: diff --git a/feature_integration_tests/baselibs_integration_tests/BUILD b/feature_integration_tests/baselibs_integration_tests/BUILD new file mode 100644 index 00000000000..3dae9d8c364 --- /dev/null +++ b/feature_integration_tests/baselibs_integration_tests/BUILD @@ -0,0 +1,91 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +load("@rules_python//python:defs.bzl", "py_library") +load("@score_itf//:defs.bzl", "py_itf_test") + +package(default_visibility = ["//feature_integration_tests/baselibs_integration_tests:__subpackages__"]) + +config_setting( + name = "config_linux_x86_64", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + "@score_bazel_platforms//runtime_es:posix", + ], +) + +config_setting( + name = "config_qnx_x86_64", + constraint_values = [ + "@platforms//os:qnx", + "@platforms//cpu:x86_64", + "@score_bazel_platforms//runtime_es:posix", + ], +) + +py_library( + name = "demo_runner", + srcs = ["demo_runner.py"], + # Lets the test `import demo_runner` as a flat top-level module. + imports = ["."], +) + +py_itf_test( + name = "bit", + srcs = ["test_baselibs_features.py"], + args = select( + { + ":config_linux_x86_64": [ + "--docker-image-bootstrap=$(location //images/linux_x86_64:image_load)", + "--docker-image=score_showcases:latest", + ], + ":config_qnx_x86_64": [ + "--qemu-config=$(location //feature_integration_tests/configs:qemu_bridge_config.json)", + "--qemu-image=$(location //images/qnx_x86_64:image)", + ], + }, + no_match_error = "Unsupported platform", + ), + data = select({ + ":config_linux_x86_64": [ + "//images/linux_x86_64:image_load", + ], + ":config_qnx_x86_64": [ + "//feature_integration_tests/configs:qemu_bridge_config.json", + "//images/qnx_x86_64:image", + ], + }), + plugins = select( + { + ":config_linux_x86_64": [ + "@score_itf//score/itf/plugins:docker_plugin", + ], + ":config_qnx_x86_64": [ + "@score_itf//score/itf/plugins:qemu_plugin", + ], + }, + no_match_error = "Unsupported platform", + ) + [ + # Records PartiallyVerifies (via the `@add_test_properties` decorator) + # into the JUnit XML. docs-as-code's source code linker turns those + # properties into `testlink` attributes on the referenced baselibs + # feature requirement needs. + "@score_itf//score/itf/plugins:attribute_plugin", + ], + tags = [ + "manual", + ], + deps = [ + ":demo_runner", + ], +) diff --git a/feature_integration_tests/baselibs_integration_tests/demo/BUILD b/feature_integration_tests/baselibs_integration_tests/demo/BUILD new file mode 100644 index 00000000000..ed6ef01da55 --- /dev/null +++ b/feature_integration_tests/baselibs_integration_tests/demo/BUILD @@ -0,0 +1,33 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_cc//cc:defs.bzl", "cc_binary") + +# On-target demo exercising one baselibs feature library per subcommand. It is +# bundled into //images/linux_x86_64:image (see that package's BUILD) and driven +# by the baselibs integration tests in the parent package. +cc_binary( + name = "baselibs_feature_demo", + srcs = ["main.cpp"], + tags = ["manual"], + visibility = ["//visibility:public"], + deps = [ + "@score_baselibs//score/bitmanipulation", + "@score_baselibs//score/containers:dynamic_array", + "@score_baselibs//score/hash/code/crc:crc_ieee", + "@score_baselibs//score/json", + "@score_baselibs//score/language/futurecpp", + "@score_baselibs//score/result", + "@score_baselibs//score/utils:base64", + ], +) diff --git a/feature_integration_tests/baselibs_integration_tests/demo/main.cpp b/feature_integration_tests/baselibs_integration_tests/demo/main.cpp new file mode 100644 index 00000000000..5b4b43e24aa --- /dev/null +++ b/feature_integration_tests/baselibs_integration_tests/demo/main.cpp @@ -0,0 +1,218 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +// On-target feature demo for the Eclipse S-CORE base libraries. +// +// Each subcommand exercises exactly one baselibs feature library and prints a +// single deterministic ": ..." line to stdout. The baselibs integration +// tests in feature_integration_tests/baselibs_integration_tests run these +// subcommands on the deployed target image and assert on the printed line, +// thereby verifying the corresponding feat_req__baselibs__* feature +// requirement. + +#include "score/bitmanipulation/bit_manipulation.h" +#include "score/containers/dynamic_array.h" +#include "score/hash/code/crc/crc32_ieee.h" +#include "score/json/json_parser.h" +#include "score/result/error.h" +#include "score/result/result.h" +#include "score/utils/base64.h" + +#include "score/span.hpp" + +#include +#include +#include +#include +#include + +namespace +{ + +// Minimal self-contained error domain so the result demo can exercise the +// error path without pulling in a logging backend. +enum class DemoErrc : score::result::ErrorCode +{ + kDivideByZero = 1, +}; + +class DemoErrorDomain final : public score::result::ErrorDomain +{ + public: + std::string_view MessageFor(const score::result::ErrorCode& code) const noexcept override + { + return (static_cast(code) == DemoErrc::kDivideByZero) ? "division by zero" : "unknown error"; + } +}; + +constexpr DemoErrorDomain kDemoErrorDomain; + +score::result::Error MakeError(const DemoErrc code, const std::string_view user_message = "") noexcept +{ + return score::result::Error{static_cast(code), kDemoErrorDomain, user_message}; +} + +score::Result SafeDivide(const int numerator, const int denominator) +{ + if (denominator == 0) + { + return score::MakeUnexpected(DemoErrc::kDivideByZero, "cannot divide by zero"); + } + return numerator / denominator; +} + +// feat_req__baselibs__json_library +int RunJson() +{ + static constexpr char kDocument[] = R"({"name":"baselibs","version":2,"libs":["json","result"]})"; + + const score::json::JsonParser parser; + const auto root = parser.FromBuffer(kDocument); + if (!root.has_value()) + { + std::cout << "json: parse_error\n"; + return 1; + } + + const auto& object_result = root.value().As(); + if (!object_result.has_value()) + { + std::cout << "json: not_an_object\n"; + return 1; + } + const auto& object = object_result.value().get(); + + const auto name = object.find("name")->second.As(); + const auto version = object.find("version")->second.As(); + const auto libs = object.find("libs")->second.As(); + + std::cout << "json: name=" << name.value().get() << " version=" << version.value() + << " libs_count=" << libs.value().get().size() << "\n"; + return 0; +} + +// feat_req__baselibs__utils_library +int RunBase64() +{ + const std::vector input{'f', 'o', 'o', 'b', 'a', 'r'}; + const std::string encoded = score::utils::EncodeBase64(input); + const std::vector decoded = score::utils::DecodeBase64(encoded); + const bool roundtrip = (decoded == input); + + const std::string decoded_text{decoded.begin(), decoded.end()}; + std::cout << "base64: encoded=" << encoded << " decoded=" << decoded_text + << " roundtrip=" << (roundtrip ? "ok" : "fail") << "\n"; + return roundtrip ? 0 : 1; +} + +// feat_req__baselibs__bitmanipulation +int RunBitManip() +{ + std::uint8_t value{0U}; + score::platform::SetBit(value, 1U); // -> 0b00000010 (2) + score::platform::SetBit(value, 4U); // -> 0b00010010 (18) + const bool bit4_set = score::platform::CheckBit(value, 4U); + score::platform::ToggleBit(value, 1U); // -> 0b00010000 (16) + + std::cout << "bitmanip: value=" << static_cast(value) << " check_bit4=" << (bit4_set ? 1 : 0) + << "\n"; + return 0; +} + +// feat_req__baselibs__hash_library +int RunCrc32() +{ + const std::vector data{'1', '2', '3', '4', '5', '6', '7', '8', '9'}; + + score::hash::Crc32IeeeHashCalculator calculator; + const auto update_result = calculator.Update(score::cpp::span{data.data(), data.size()}); + if (!update_result.has_value()) + { + std::cout << "crc32: update_error\n"; + return 1; + } + const std::uint32_t checksum = static_cast(calculator.GetChecksum()); + + std::cout << "crc32: checksum=0x" << std::hex << checksum << std::dec << "\n"; + return 0; +} + +// feat_req__baselibs__containers_library +int RunContainers() +{ + score::containers::DynamicArray array{4U}; + array[0U] = 10; + array[1U] = 20; + array[2U] = 30; + array[3U] = 40; + + int sum{0}; + for (const int element : array) + { + sum += element; + } + + std::cout << "containers: size=" << array.size() << " sum=" << sum << "\n"; + return 0; +} + +// feat_req__baselibs__result_library +int RunResult() +{ + const auto ok = SafeDivide(84, 2); + const auto err = SafeDivide(1, 0); + + std::cout << "result: ok_value=" << ok.value() << " error_handled=" << (err.has_value() ? 0 : 1) + << " error_msg=" << err.error().Message() << "\n"; + return 0; +} + +} // namespace + +int main(int argc, char** argv) +{ + if (argc < 2) + { + std::cerr << "usage: baselibs_feature_demo \n"; + return 2; + } + + const std::string_view command{argv[1]}; + if (command == "json") + { + return RunJson(); + } + if (command == "base64") + { + return RunBase64(); + } + if (command == "bitmanip") + { + return RunBitManip(); + } + if (command == "crc32") + { + return RunCrc32(); + } + if (command == "containers") + { + return RunContainers(); + } + if (command == "result") + { + return RunResult(); + } + + std::cerr << "unknown command: " << command << "\n"; + return 2; +} diff --git a/feature_integration_tests/baselibs_integration_tests/demo_runner.py b/feature_integration_tests/baselibs_integration_tests/demo_runner.py new file mode 100644 index 00000000000..6a1e6229eeb --- /dev/null +++ b/feature_integration_tests/baselibs_integration_tests/demo_runner.py @@ -0,0 +1,32 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Helper for driving the baselibs feature demo on the deployed target.""" + +import logging + +logger = logging.getLogger(__name__) + +_DEMO_BIN = "/usr/bin/baselibs_feature_demo" + + +def run_demo(target, subcommand: str) -> str: + """Run one subcommand of the demo binary on the target and return its stdout. + + Asserts the binary exits successfully so every feature test fails loudly if + the exercised baselibs library misbehaves on the target. + """ + exit_code, out = target.execute(f"{_DEMO_BIN} {subcommand}") + output = out.decode(errors="replace").strip() + logger.info("baselibs_feature_demo %s -> (%s) %s", subcommand, exit_code, output) + assert exit_code == 0, f"baselibs_feature_demo {subcommand} exited with {exit_code}:\n{output}" + return output diff --git a/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py b/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py new file mode 100644 index 00000000000..b9d40dc27d1 --- /dev/null +++ b/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py @@ -0,0 +1,106 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +"""Feature integration tests for the Eclipse S-CORE base libraries. + +Each test drives one subcommand of the on-target ``baselibs_feature_demo`` +binary (see ``demo/``) on the deployed target image through ``run_demo`` and +asserts on its deterministic stdout line, thereby verifying one baselibs +feature library end-to-end on the platform. + +Each test references the ``feat_req__baselibs__*`` feature requirement it +partially verifies via ``@add_test_properties``. docs-as-code's source code +linker turns those properties into ``testlink`` attributes on the referenced +requirement needs, which surface in the baselibs feature coverage figures of +the platform verification report. + +All tests in this package are combined into the single ``bit`` suite by this +package's ``BUILD`` file, which CI runs before the documentation build. +""" + +from attribute_plugin import add_test_properties +from demo_runner import run_demo + + +@add_test_properties( + partially_verifies=["feat_req__baselibs__json_library"], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_json_library_parses_document(target): + """Parse a JSON document and read back object, number and list members.""" + output = run_demo(target, "json") + assert "name=baselibs" in output + assert "version=2" in output + assert "libs_count=2" in output + + +@add_test_properties( + partially_verifies=["feat_req__baselibs__utils_library"], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_utils_library_base64_roundtrip(target): + """Base64-encode and decode a byte buffer and confirm the round-trip.""" + output = run_demo(target, "base64") + assert "encoded=Zm9vYmFy" in output + assert "decoded=foobar" in output + assert "roundtrip=ok" in output + + +@add_test_properties( + partially_verifies=["feat_req__baselibs__bitmanipulation"], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_bitmanipulation_library_sets_and_toggles_bits(target): + """Set, check and toggle individual bits of an integral value.""" + output = run_demo(target, "bitmanip") + assert "value=16" in output + assert "check_bit4=1" in output + + +@add_test_properties( + partially_verifies=["feat_req__baselibs__hash_library"], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_hash_library_crc32_ieee_check_value(target): + """Compute the IEEE CRC-32 of the canonical "123456789" check vector.""" + output = run_demo(target, "crc32") + # IEEE 802.3 CRC-32 check value for the ASCII string "123456789". + assert "checksum=0xcbf43926" in output + + +@add_test_properties( + partially_verifies=["feat_req__baselibs__containers_library"], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_containers_library_dynamic_array(target): + """Fill and iterate a DynamicArray, confirming size and element access.""" + output = run_demo(target, "containers") + assert "size=4" in output + assert "sum=100" in output + + +@add_test_properties( + partially_verifies=["feat_req__baselibs__result_library"], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_result_library_error_handling(target): + """Exercise the Result value and error paths without C++ exceptions.""" + output = run_demo(target, "result") + assert "ok_value=42" in output + assert "error_handled=1" in output + assert "error_msg=division by zero" in output diff --git a/images/linux_x86_64/BUILD b/images/linux_x86_64/BUILD index 3eee222f6d6..927cef1afb5 100644 --- a/images/linux_x86_64/BUILD +++ b/images/linux_x86_64/BUILD @@ -47,6 +47,13 @@ pkg_tar( package_dir = "/usr/bin/datarouter/etc", ) +pkg_tar( + name = "baselibs_feature_demo_tar", + srcs = ["//feature_integration_tests/baselibs_integration_tests/demo:baselibs_feature_demo"], + mode = "0755", + package_dir = "/usr/bin", +) + oci_image( name = "image", base = "@ubuntu_22_04_linux_amd64", @@ -54,6 +61,7 @@ oci_image( "//showcases:showcases_pkg_tar", ":datarouter_bin_tar", ":datarouter_etc_tar", + ":baselibs_feature_demo_tar", ], ) @@ -62,6 +70,7 @@ oci_load( image = ":image", repo_tags = ["score_showcases:latest"], visibility = [ + "//feature_integration_tests/baselibs_integration_tests:__subpackages__", "//feature_integration_tests/itf:__pkg__", ], ) From 47c82ab439c0386e8cda2880a026413113e20192 Mon Sep 17 00:00:00 2001 From: Anton Krivoborodov Date: Thu, 1 Oct 2026 09:11:16 +0000 Subject: [PATCH 2/6] test(baselibs): rename demo application to test_app --- .../baselibs_integration_tests/BUILD | 8 ++++---- .../{demo => test_app}/BUILD | 8 ++++---- .../{demo => test_app}/main.cpp | 20 +++++++++---------- .../{demo_runner.py => test_app_runner.py} | 14 ++++++------- .../test_baselibs_features.py | 20 +++++++++---------- images/linux_x86_64/BUILD | 6 +++--- 6 files changed, 38 insertions(+), 38 deletions(-) rename feature_integration_tests/baselibs_integration_tests/{demo => test_app}/BUILD (80%) rename feature_integration_tests/baselibs_integration_tests/{demo => test_app}/main.cpp (88%) rename feature_integration_tests/baselibs_integration_tests/{demo_runner.py => test_app_runner.py} (62%) diff --git a/feature_integration_tests/baselibs_integration_tests/BUILD b/feature_integration_tests/baselibs_integration_tests/BUILD index 3dae9d8c364..78907dc3baf 100644 --- a/feature_integration_tests/baselibs_integration_tests/BUILD +++ b/feature_integration_tests/baselibs_integration_tests/BUILD @@ -34,9 +34,9 @@ config_setting( ) py_library( - name = "demo_runner", - srcs = ["demo_runner.py"], - # Lets the test `import demo_runner` as a flat top-level module. + name = "test_app_runner", + srcs = ["test_app_runner.py"], + # Lets the test `import test_app_runner` as a flat top-level module. imports = ["."], ) @@ -86,6 +86,6 @@ py_itf_test( "manual", ], deps = [ - ":demo_runner", + ":test_app_runner", ], ) diff --git a/feature_integration_tests/baselibs_integration_tests/demo/BUILD b/feature_integration_tests/baselibs_integration_tests/test_app/BUILD similarity index 80% rename from feature_integration_tests/baselibs_integration_tests/demo/BUILD rename to feature_integration_tests/baselibs_integration_tests/test_app/BUILD index ed6ef01da55..bc47d25a4bc 100644 --- a/feature_integration_tests/baselibs_integration_tests/demo/BUILD +++ b/feature_integration_tests/baselibs_integration_tests/test_app/BUILD @@ -13,11 +13,11 @@ load("@rules_cc//cc:defs.bzl", "cc_binary") -# On-target demo exercising one baselibs feature library per subcommand. It is -# bundled into //images/linux_x86_64:image (see that package's BUILD) and driven -# by the baselibs integration tests in the parent package. +# On-target test app exercising one baselibs feature library per subcommand. It +# is bundled into //images/linux_x86_64:image (see that package's BUILD) and +# driven by the baselibs integration tests in the parent package. cc_binary( - name = "baselibs_feature_demo", + name = "baselibs_test_app", srcs = ["main.cpp"], tags = ["manual"], visibility = ["//visibility:public"], diff --git a/feature_integration_tests/baselibs_integration_tests/demo/main.cpp b/feature_integration_tests/baselibs_integration_tests/test_app/main.cpp similarity index 88% rename from feature_integration_tests/baselibs_integration_tests/demo/main.cpp rename to feature_integration_tests/baselibs_integration_tests/test_app/main.cpp index 5b4b43e24aa..6c6cb942249 100644 --- a/feature_integration_tests/baselibs_integration_tests/demo/main.cpp +++ b/feature_integration_tests/baselibs_integration_tests/test_app/main.cpp @@ -11,7 +11,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -// On-target feature demo for the Eclipse S-CORE base libraries. +// On-target test app for the Eclipse S-CORE base libraries. // // Each subcommand exercises exactly one baselibs feature library and prints a // single deterministic ": ..." line to stdout. The baselibs integration @@ -39,34 +39,34 @@ namespace { -// Minimal self-contained error domain so the result demo can exercise the +// Minimal self-contained error domain so the result subcommand can exercise the // error path without pulling in a logging backend. -enum class DemoErrc : score::result::ErrorCode +enum class AppErrc : score::result::ErrorCode { kDivideByZero = 1, }; -class DemoErrorDomain final : public score::result::ErrorDomain +class AppErrorDomain final : public score::result::ErrorDomain { public: std::string_view MessageFor(const score::result::ErrorCode& code) const noexcept override { - return (static_cast(code) == DemoErrc::kDivideByZero) ? "division by zero" : "unknown error"; + return (static_cast(code) == AppErrc::kDivideByZero) ? "division by zero" : "unknown error"; } }; -constexpr DemoErrorDomain kDemoErrorDomain; +constexpr AppErrorDomain kAppErrorDomain; -score::result::Error MakeError(const DemoErrc code, const std::string_view user_message = "") noexcept +score::result::Error MakeError(const AppErrc code, const std::string_view user_message = "") noexcept { - return score::result::Error{static_cast(code), kDemoErrorDomain, user_message}; + return score::result::Error{static_cast(code), kAppErrorDomain, user_message}; } score::Result SafeDivide(const int numerator, const int denominator) { if (denominator == 0) { - return score::MakeUnexpected(DemoErrc::kDivideByZero, "cannot divide by zero"); + return score::MakeUnexpected(AppErrc::kDivideByZero, "cannot divide by zero"); } return numerator / denominator; } @@ -183,7 +183,7 @@ int main(int argc, char** argv) { if (argc < 2) { - std::cerr << "usage: baselibs_feature_demo \n"; + std::cerr << "usage: baselibs_test_app \n"; return 2; } diff --git a/feature_integration_tests/baselibs_integration_tests/demo_runner.py b/feature_integration_tests/baselibs_integration_tests/test_app_runner.py similarity index 62% rename from feature_integration_tests/baselibs_integration_tests/demo_runner.py rename to feature_integration_tests/baselibs_integration_tests/test_app_runner.py index 6a1e6229eeb..a7bd1333e57 100644 --- a/feature_integration_tests/baselibs_integration_tests/demo_runner.py +++ b/feature_integration_tests/baselibs_integration_tests/test_app_runner.py @@ -10,23 +10,23 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -"""Helper for driving the baselibs feature demo on the deployed target.""" +"""Helper for driving the baselibs test app on the deployed target.""" import logging logger = logging.getLogger(__name__) -_DEMO_BIN = "/usr/bin/baselibs_feature_demo" +_TEST_APP_BIN = "/usr/bin/baselibs_test_app" -def run_demo(target, subcommand: str) -> str: - """Run one subcommand of the demo binary on the target and return its stdout. +def run_test_app(target, subcommand: str) -> str: + """Run one subcommand of the test app on the target and return its stdout. Asserts the binary exits successfully so every feature test fails loudly if the exercised baselibs library misbehaves on the target. """ - exit_code, out = target.execute(f"{_DEMO_BIN} {subcommand}") + exit_code, out = target.execute(f"{_TEST_APP_BIN} {subcommand}") output = out.decode(errors="replace").strip() - logger.info("baselibs_feature_demo %s -> (%s) %s", subcommand, exit_code, output) - assert exit_code == 0, f"baselibs_feature_demo {subcommand} exited with {exit_code}:\n{output}" + logger.info("baselibs_test_app %s -> (%s) %s", subcommand, exit_code, output) + assert exit_code == 0, f"baselibs_test_app {subcommand} exited with {exit_code}:\n{output}" return output diff --git a/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py b/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py index b9d40dc27d1..88553dca2bb 100644 --- a/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py +++ b/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py @@ -12,9 +12,9 @@ # ******************************************************************************* """Feature integration tests for the Eclipse S-CORE base libraries. -Each test drives one subcommand of the on-target ``baselibs_feature_demo`` -binary (see ``demo/``) on the deployed target image through ``run_demo`` and -asserts on its deterministic stdout line, thereby verifying one baselibs +Each test drives one subcommand of the on-target ``baselibs_test_app`` +binary (see ``test_app/``) on the deployed target image through ``run_test_app`` +and asserts on its deterministic stdout line, thereby verifying one baselibs feature library end-to-end on the platform. Each test references the ``feat_req__baselibs__*`` feature requirement it @@ -28,7 +28,7 @@ """ from attribute_plugin import add_test_properties -from demo_runner import run_demo +from test_app_runner import run_test_app @add_test_properties( @@ -38,7 +38,7 @@ ) def test_json_library_parses_document(target): """Parse a JSON document and read back object, number and list members.""" - output = run_demo(target, "json") + output = run_test_app(target, "json") assert "name=baselibs" in output assert "version=2" in output assert "libs_count=2" in output @@ -51,7 +51,7 @@ def test_json_library_parses_document(target): ) def test_utils_library_base64_roundtrip(target): """Base64-encode and decode a byte buffer and confirm the round-trip.""" - output = run_demo(target, "base64") + output = run_test_app(target, "base64") assert "encoded=Zm9vYmFy" in output assert "decoded=foobar" in output assert "roundtrip=ok" in output @@ -64,7 +64,7 @@ def test_utils_library_base64_roundtrip(target): ) def test_bitmanipulation_library_sets_and_toggles_bits(target): """Set, check and toggle individual bits of an integral value.""" - output = run_demo(target, "bitmanip") + output = run_test_app(target, "bitmanip") assert "value=16" in output assert "check_bit4=1" in output @@ -76,7 +76,7 @@ def test_bitmanipulation_library_sets_and_toggles_bits(target): ) def test_hash_library_crc32_ieee_check_value(target): """Compute the IEEE CRC-32 of the canonical "123456789" check vector.""" - output = run_demo(target, "crc32") + output = run_test_app(target, "crc32") # IEEE 802.3 CRC-32 check value for the ASCII string "123456789". assert "checksum=0xcbf43926" in output @@ -88,7 +88,7 @@ def test_hash_library_crc32_ieee_check_value(target): ) def test_containers_library_dynamic_array(target): """Fill and iterate a DynamicArray, confirming size and element access.""" - output = run_demo(target, "containers") + output = run_test_app(target, "containers") assert "size=4" in output assert "sum=100" in output @@ -100,7 +100,7 @@ def test_containers_library_dynamic_array(target): ) def test_result_library_error_handling(target): """Exercise the Result value and error paths without C++ exceptions.""" - output = run_demo(target, "result") + output = run_test_app(target, "result") assert "ok_value=42" in output assert "error_handled=1" in output assert "error_msg=division by zero" in output diff --git a/images/linux_x86_64/BUILD b/images/linux_x86_64/BUILD index 927cef1afb5..66d2c2e29ed 100644 --- a/images/linux_x86_64/BUILD +++ b/images/linux_x86_64/BUILD @@ -48,8 +48,8 @@ pkg_tar( ) pkg_tar( - name = "baselibs_feature_demo_tar", - srcs = ["//feature_integration_tests/baselibs_integration_tests/demo:baselibs_feature_demo"], + name = "baselibs_test_app_tar", + srcs = ["//feature_integration_tests/baselibs_integration_tests/test_app:baselibs_test_app"], mode = "0755", package_dir = "/usr/bin", ) @@ -61,7 +61,7 @@ oci_image( "//showcases:showcases_pkg_tar", ":datarouter_bin_tar", ":datarouter_etc_tar", - ":baselibs_feature_demo_tar", + ":baselibs_test_app_tar", ], ) From da15b35f46a8060e63469c302183179d177c2c84 Mon Sep 17 00:00:00 2001 From: Anton Krivoborodov Date: Thu, 1 Oct 2026 09:51:30 +0000 Subject: [PATCH 3/6] ci: add throwaway push-triggered preview workflow for baselibs coverage Delete before merge. Proves the baselibs feature integration tests feed the platform verification report, which the pull_request_target pipeline cannot show pre-merge. --- .../zz-preview-baselibs-coverage.yml | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 .github/workflows/zz-preview-baselibs-coverage.yml diff --git a/.github/workflows/zz-preview-baselibs-coverage.yml b/.github/workflows/zz-preview-baselibs-coverage.yml new file mode 100644 index 00000000000..4551bdcea3b --- /dev/null +++ b/.github/workflows/zz-preview-baselibs-coverage.yml @@ -0,0 +1,122 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +# THROWAWAY preview workflow - DELETE before merging this PR. +# +# The real wiring lives in test_and_docs.yml, but that job runs via +# `pull_request_target`, which reads its YAML from the base branch (main) - so +# the new "Execute Baselibs Integration Tests" step added on this branch never +# runs in the PR. A `push` event instead reads the YAML from the pushed commit, +# so this workflow actually exercises the new step and proves the baselibs +# feature requirement coverage shows up in the platform verification report. +name: Preview - Baselibs Coverage (throwaway) +on: + push: + branches: + - feat/baselibs-integration-tests +concurrency: + # push events have no pull_request.number, so key on the ref. + group: preview-baselibs-coverage-${{ github.ref }} + cancel-in-progress: true +permissions: + contents: read +jobs: + preview: + runs-on: ${{ vars.REPO_RUNNER_LABELS && fromJSON(vars.REPO_RUNNER_LABELS) || 'ubuntu-latest' }} + steps: + - name: Clean disk space + uses: eclipse-score/more-disk-space@v1.1 + with: + level: 4 + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Set up Docker Buildx + # Required by the baselibs integration tests, which deploy the test app + # via a Docker target built from //images/linux_x86_64:image. + uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.18.0 + with: + bazelisk-cache: true + disk-cache: ${{ github.workflow }} + repository-cache: | + - MODULE.bazel + - known_good.json + - bazel_common/*.MODULE.bazel + - patches/** + - .bazelrc + cache-save: false + - name: Set up Python 3 + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install Graphviz + uses: eclipse-score/apt-install@main + with: + packages: graphviz + cache: false + - name: Execute Baselibs Integration Tests + # Must run before the documentation build and in this same workspace: + # the docs source code linker scans `bazel-testlogs` for the JUnit XML + # that carries the PartiallyVerifies properties. + run: | + bazel test --lockfile_mode=error --config=linux-x86_64 \ + //feature_integration_tests/baselibs_integration_tests:bit + - name: Show recorded PartiallyVerifies properties + if: always() + run: | + echo "## baselibs test.xml PartiallyVerifies:" + find bazel-testlogs -path '*baselibs_integration_tests*' -name 'test.xml' \ + -exec sh -c 'echo "--- $1 ---"; grep -o "feat_req__baselibs__[a-z_]*" "$1" | sort -u' _ {} \; + - name: Build documentation + run: | + bazel run \ + --lockfile_mode=error \ + //:docs_shim -- \ + --github_user=${{ github.repository_owner }} \ + --github_repo=${{ github.event.repository.name }} + - name: Verify baselibs feature requirements are linked by tests + run: | + set -e + echo "## Searching rendered docs for baselibs test links..." + missing=0 + for t in \ + test_json_library_parses_document \ + test_utils_library_base64_roundtrip \ + test_bitmanipulation_library_sets_and_toggles_bits \ + test_hash_library_crc32_ieee_check_value \ + test_containers_library_dynamic_array \ + test_result_library_error_handling; do + if grep -rq "$t" _build; then + echo "OK $t is referenced in the rendered docs" + else + echo "MISS $t is NOT referenced in the rendered docs" + missing=1 + fi + done + echo "## feat_req__baselibs__* occurrences carrying a testlink in needs.json:" + grep -o "feat_req__baselibs__[a-z_]*" _build/needs.json 2>/dev/null | sort -u || true + if [ "$missing" -ne 0 ]; then + echo "::error::At least one baselibs feature test is not linked in the report" + exit 1 + fi + - name: Upload rendered documentation (report preview) + if: always() + run: tar -cf baselibs-report-preview.tar _build + - name: Upload report artifact + if: always() + uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + with: + name: baselibs-report-preview-${{ github.sha }} + path: baselibs-report-preview.tar + retention-days: 3 + if-no-files-found: error From 30bf48863d3581c241f3dbb25b642fd7deb738b9 Mon Sep 17 00:00:00 2001 From: Anton Krivoborodov Date: Thu, 1 Oct 2026 12:16:31 +0000 Subject: [PATCH 4/6] test(baselibs): cover remaining baselibs feature requirements Add on-target subcommands and ITF tests for the concurrency, filesystem, memory, static reflection and flatbuffers libraries, and extend the containers and result tests to also partially verify abi_containers and panic_free_development. Together with the existing tests this now covers all 14 feat_req__baselibs__* feature requirements. --- .../baselibs_integration_tests/test_app/BUILD | 18 ++- .../test_app/baselibs_sample.fbs | 29 ++++ .../test_app/main.cpp | 136 +++++++++++++++++- .../test_baselibs_features.py | 95 +++++++++++- 4 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 feature_integration_tests/baselibs_integration_tests/test_app/baselibs_sample.fbs diff --git a/feature_integration_tests/baselibs_integration_tests/test_app/BUILD b/feature_integration_tests/baselibs_integration_tests/test_app/BUILD index bc47d25a4bc..7185e9623e3 100644 --- a/feature_integration_tests/baselibs_integration_tests/test_app/BUILD +++ b/feature_integration_tests/baselibs_integration_tests/test_app/BUILD @@ -12,22 +12,38 @@ # ******************************************************************************* load("@rules_cc//cc:defs.bzl", "cc_binary") +load("@score_baselibs//score/flatbuffers/bazel:codegen.bzl", "generate_cpp") + +# Generated C++ header for the flatbuffers subcommand's sample schema. +generate_cpp( + name = "gen_baselibs_sample_h", + output = "baselibs_sample_generated.h", + schema = "baselibs_sample.fbs", +) # On-target test app exercising one baselibs feature library per subcommand. It # is bundled into //images/linux_x86_64:image (see that package's BUILD) and # driven by the baselibs integration tests in the parent package. cc_binary( name = "baselibs_test_app", - srcs = ["main.cpp"], + srcs = [ + "main.cpp", + ":gen_baselibs_sample_h", + ], tags = ["manual"], visibility = ["//visibility:public"], deps = [ "@score_baselibs//score/bitmanipulation", + "@score_baselibs//score/concurrency:notification", "@score_baselibs//score/containers:dynamic_array", + "@score_baselibs//score/filesystem", + "@score_baselibs//score/flatbuffers:flatbufferscpp", "@score_baselibs//score/hash/code/crc:crc_ieee", "@score_baselibs//score/json", "@score_baselibs//score/language/futurecpp", + "@score_baselibs//score/memory:pmr_ring_buffer", "@score_baselibs//score/result", + "@score_baselibs//score/static_reflection_with_serialization/serialization", "@score_baselibs//score/utils:base64", ], ) diff --git a/feature_integration_tests/baselibs_integration_tests/test_app/baselibs_sample.fbs b/feature_integration_tests/baselibs_integration_tests/test_app/baselibs_sample.fbs new file mode 100644 index 00000000000..984e394d652 --- /dev/null +++ b/feature_integration_tests/baselibs_integration_tests/test_app/baselibs_sample.fbs @@ -0,0 +1,29 @@ +// ****************************************************************************** +// Copyright (c) 2026 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ****************************************************************************** + +// Minimal FlatBuffers schema for the baselibs flatbuffers feature test. A keyed +// `Item` table lets the generated code build a sorted vector and look items up +// by key, which exercises the schema-generated C++ API end-to-end. + +namespace score.ri.baselibs_sample; + +table Item { + id:int (key); + label:string; +} + +table Container { + items:[Item]; +} + +root_type Container; diff --git a/feature_integration_tests/baselibs_integration_tests/test_app/main.cpp b/feature_integration_tests/baselibs_integration_tests/test_app/main.cpp index 6c6cb942249..401182cde1d 100644 --- a/feature_integration_tests/baselibs_integration_tests/test_app/main.cpp +++ b/feature_integration_tests/baselibs_integration_tests/test_app/main.cpp @@ -21,21 +21,45 @@ // requirement. #include "score/bitmanipulation/bit_manipulation.h" +#include "score/concurrency/notification.h" #include "score/containers/dynamic_array.h" +#include "score/filesystem/path.h" #include "score/hash/code/crc/crc32_ieee.h" #include "score/json/json_parser.h" +#include "score/memory/pmr_ring_buffer.h" #include "score/result/error.h" #include "score/result/result.h" #include "score/utils/base64.h" +#include "static_reflection_with_serialization/serialization/for_logging.h" + +#include "flatbuffers/flatbuffer_builder.h" +#include "feature_integration_tests/baselibs_integration_tests/test_app/baselibs_sample_generated.h" #include "score/span.hpp" +#include "score/stop_token.hpp" +#include #include #include #include #include #include +// feat_req__baselibs__static_reflection_library +// A reflectable payload for the serialization round-trip. SCORE_STRUCT_VISITABLE +// must be invoked in the struct's own namespace so the generated visit_as() is +// found by argument-dependent lookup. +namespace baselibs_sample +{ +struct ReflectPayload +{ + std::uint32_t id; + std::uint16_t count; +}; + +SCORE_STRUCT_VISITABLE(ReflectPayload, id, count) +} // namespace baselibs_sample + namespace { @@ -166,7 +190,7 @@ int RunContainers() return 0; } -// feat_req__baselibs__result_library +// feat_req__baselibs__result_library, feat_req__baselibs__panic_free_development int RunResult() { const auto ok = SafeDivide(84, 2); @@ -177,13 +201,101 @@ int RunResult() return 0; } +// feat_req__baselibs__concurrency_library +int RunConcurrency() +{ + score::concurrency::Notification notification; + notification.notify(); + // Already notified, so the wait returns true immediately without blocking. + const bool notified = notification.waitWithAbort(score::cpp::stop_token{}); + notification.reset(); + + std::cout << "concurrency: notified=" << (notified ? 1 : 0) << " reset=ok\n"; + return notified ? 0 : 1; +} + +// feat_req__baselibs__filesystem_library +int RunFilesystem() +{ + const score::filesystem::Path path{"/home/user/documents/report.txt"}; + + std::cout << "filesystem: filename=" << path.Filename().Native() + << " extension=" << path.Extension().Native() + << " parent=" << path.ParentPath().Native() << "\n"; + return 0; +} + +// feat_req__baselibs__memory_library +int RunMemory() +{ + score::memory::PmrRingBuffer buffer{3U, score::cpp::pmr::polymorphic_allocator<>()}; + buffer.emplace_back(10); + buffer.emplace_back(20); + buffer.emplace_back(30); + const bool full = buffer.full(); + buffer.emplace_back(40); // overwrites the oldest element (10) + + std::cout << "memory: size=" << buffer.size() << " front=" << buffer.front() + << " full=" << (full ? 1 : 0) << "\n"; + return 0; +} + +// feat_req__baselibs__static_reflection_library +int RunReflect() +{ + using serializer = score::common::visitor::logging_serializer; + + const baselibs_sample::ReflectPayload original{0xABCDU, 7U}; + std::array buffer{}; + + const auto size = serializer::serialize(original, buffer.data(), buffer.size()); + + baselibs_sample::ReflectPayload restored{}; + const bool ok = static_cast(serializer::deserialize(buffer.data(), size, restored)); + const bool match = ok && (restored.id == original.id) && (restored.count == original.count); + + std::cout << "reflect: bytes=" << static_cast(size) << " id=" << restored.id + << " count=" << restored.count << " roundtrip=" << (match ? "ok" : "fail") << "\n"; + return match ? 0 : 1; +} + +// feat_req__baselibs__flatbuffers_library, feat_req__baselibs__multi_language_apis +int RunFlatbuffers() +{ + namespace sample = score::ri::baselibs_sample; + + flatbuffers::FlatBufferBuilder builder{256U}; + std::vector> items{ + sample::CreateItemDirect(builder, 30, "thirty"), + sample::CreateItemDirect(builder, 10, "ten"), + sample::CreateItemDirect(builder, 20, "twenty"), + }; + const auto items_vector = builder.CreateVectorOfSortedTables(&items); + const auto container = sample::CreateContainer(builder, items_vector); + builder.Finish(container); + + const sample::Container* root = sample::GetContainer(builder.GetBufferPointer()); + const sample::Item* found = root->items()->LookupByKey(20); + if (found == nullptr) + { + std::cout << "flatbuffers: lookup_error\n"; + return 1; + } + + std::cout << "flatbuffers: items=" << root->items()->size() << " lookup20=" << found->label()->c_str() + << "\n"; + return 0; +} + } // namespace int main(int argc, char** argv) { if (argc < 2) { - std::cerr << "usage: baselibs_test_app \n"; + std::cerr << "usage: baselibs_test_app " + "\n"; return 2; } @@ -212,6 +324,26 @@ int main(int argc, char** argv) { return RunResult(); } + if (command == "concurrency") + { + return RunConcurrency(); + } + if (command == "filesystem") + { + return RunFilesystem(); + } + if (command == "memory") + { + return RunMemory(); + } + if (command == "reflect") + { + return RunReflect(); + } + if (command == "flatbuffers") + { + return RunFlatbuffers(); + } std::cerr << "unknown command: " << command << "\n"; return 2; diff --git a/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py b/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py index 88553dca2bb..bf527d56d1d 100644 --- a/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py +++ b/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py @@ -82,25 +82,112 @@ def test_hash_library_crc32_ieee_check_value(target): @add_test_properties( - partially_verifies=["feat_req__baselibs__containers_library"], + partially_verifies=[ + "feat_req__baselibs__containers_library", + "feat_req__baselibs__abi_containers", + ], test_type="requirements-based", derivation_technique="requirements-analysis", ) def test_containers_library_dynamic_array(target): - """Fill and iterate a DynamicArray, confirming size and element access.""" + """Fill and iterate a DynamicArray, confirming size and element access. + + DynamicArray is the ABI-stable container (raw-pointer iterators, + allocator-aware), so this also partially verifies the abi_containers + requirement. + """ output = run_test_app(target, "containers") assert "size=4" in output assert "sum=100" in output @add_test_properties( - partially_verifies=["feat_req__baselibs__result_library"], + partially_verifies=[ + "feat_req__baselibs__result_library", + "feat_req__baselibs__panic_free_development", + ], test_type="requirements-based", derivation_technique="requirements-analysis", ) def test_result_library_error_handling(target): - """Exercise the Result value and error paths without C++ exceptions.""" + """Exercise the Result value and error paths without C++ exceptions. + + Returning errors as values instead of throwing is the panic-free error + handling mechanism, so this also partially verifies the + panic_free_development requirement. + """ output = run_test_app(target, "result") assert "ok_value=42" in output assert "error_handled=1" in output assert "error_msg=division by zero" in output + + +@add_test_properties( + partially_verifies=["feat_req__baselibs__concurrency_library"], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_concurrency_library_notification(target): + """Notify a Notification and confirm an already-notified wait returns.""" + output = run_test_app(target, "concurrency") + assert "notified=1" in output + assert "reset=ok" in output + + +@add_test_properties( + partially_verifies=["feat_req__baselibs__filesystem_library"], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_filesystem_library_path_decomposition(target): + """Decompose a path into filename, extension and parent components.""" + output = run_test_app(target, "filesystem") + assert "filename=report.txt" in output + assert "extension=.txt" in output + assert "parent=/home/user/documents" in output + + +@add_test_properties( + partially_verifies=["feat_req__baselibs__memory_library"], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_memory_library_pmr_ring_buffer(target): + """Fill a PMR ring buffer past capacity and confirm it overwrites oldest.""" + output = run_test_app(target, "memory") + assert "size=3" in output + assert "front=20" in output + assert "full=1" in output + + +@add_test_properties( + partially_verifies=["feat_req__baselibs__static_reflection_library"], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_static_reflection_library_serialize_roundtrip(target): + """Serialize a reflectable struct to bytes and deserialize it back.""" + output = run_test_app(target, "reflect") + assert "id=43981" in output # 0xABCD + assert "count=7" in output + assert "roundtrip=ok" in output + + +@add_test_properties( + partially_verifies=[ + "feat_req__baselibs__flatbuffers_library", + "feat_req__baselibs__multi_language_apis", + ], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_flatbuffers_library_build_and_lookup(target): + """Build a flatbuffer from a schema and look an element up by key. + + FlatBuffers is a language-neutral serialization format with generated C++ + and Rust APIs, so this also partially verifies the multi_language_apis + requirement. + """ + output = run_test_app(target, "flatbuffers") + assert "items=3" in output + assert "lookup20=twenty" in output From 9478d36e99a6264eacd82895dd79ea8a16cd13e6 Mon Sep 17 00:00:00 2001 From: Anton Krivoborodov Date: Thu, 1 Oct 2026 12:17:02 +0000 Subject: [PATCH 5/6] ci(preview): verify all 11 baselibs tests are linked (throwaway) --- .github/workflows/zz-preview-baselibs-coverage.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/zz-preview-baselibs-coverage.yml b/.github/workflows/zz-preview-baselibs-coverage.yml index 4551bdcea3b..0b73f9ce65e 100644 --- a/.github/workflows/zz-preview-baselibs-coverage.yml +++ b/.github/workflows/zz-preview-baselibs-coverage.yml @@ -95,7 +95,12 @@ jobs: test_bitmanipulation_library_sets_and_toggles_bits \ test_hash_library_crc32_ieee_check_value \ test_containers_library_dynamic_array \ - test_result_library_error_handling; do + test_result_library_error_handling \ + test_concurrency_library_notification \ + test_filesystem_library_path_decomposition \ + test_memory_library_pmr_ring_buffer \ + test_static_reflection_library_serialize_roundtrip \ + test_flatbuffers_library_build_and_lookup; do if grep -rq "$t" _build; then echo "OK $t is referenced in the rendered docs" else From 3bb3e5944636c3332feabd98041fa55775c91114 Mon Sep 17 00:00:00 2001 From: Anton Krivoborodov Date: Fri, 2 Oct 2026 10:53:28 +0000 Subject: [PATCH 6/6] ci: drop throwaway preview workflow, trim baselibs step comments Remove the push-triggered baselibs coverage preview (its job was done) and shorten the inline descriptions of the Docker Buildx and baselibs test steps. --- .github/workflows/test_and_docs.yml | 13 +- .../zz-preview-baselibs-coverage.yml | 127 ------------------ 2 files changed, 4 insertions(+), 136 deletions(-) delete mode 100644 .github/workflows/zz-preview-baselibs-coverage.yml diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/test_and_docs.yml index ba8684a4206..826a4ebb470 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -53,9 +53,7 @@ jobs: if: ${{ github.event_name != 'pull_request_target' }} uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Docker Buildx - # Required by the baselibs integration tests below, which deploy the - # baselibs feature demo via a Docker target built from - # //images/linux_x86_64:image. + # Required by the baselibs integration tests below. uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 - name: Setup Bazel uses: bazel-contrib/setup-bazel@0.18.0 @@ -105,12 +103,9 @@ jobs: run: | bazel test --config=linux-x86_64 //feature_integration_tests/test_cases:fit - name: Execute Baselibs Integration Tests - # Runs in this job - and before the documentation build below - on purpose: - # the source code linker of the documentation build scans `bazel-testlogs` - # of this very workspace. Only tests executed here contribute their - # PartiallyVerifies/FullyVerifies properties as `testlink` attributes to - # the baselibs feature requirement needs, and therefore to the test - # coverage figures of the platform verification report. + # Must run in this job, before the docs build: its source code linker + # turns the tests' Verifies properties into `testlink`s on the baselibs + # feature requirements. run: | bazel test --lockfile_mode=error --config=linux-x86_64 //feature_integration_tests/baselibs_integration_tests:bit - name: Install uv (required for SBOM license scanning) diff --git a/.github/workflows/zz-preview-baselibs-coverage.yml b/.github/workflows/zz-preview-baselibs-coverage.yml deleted file mode 100644 index 0b73f9ce65e..00000000000 --- a/.github/workflows/zz-preview-baselibs-coverage.yml +++ /dev/null @@ -1,127 +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 -# ******************************************************************************* -# THROWAWAY preview workflow - DELETE before merging this PR. -# -# The real wiring lives in test_and_docs.yml, but that job runs via -# `pull_request_target`, which reads its YAML from the base branch (main) - so -# the new "Execute Baselibs Integration Tests" step added on this branch never -# runs in the PR. A `push` event instead reads the YAML from the pushed commit, -# so this workflow actually exercises the new step and proves the baselibs -# feature requirement coverage shows up in the platform verification report. -name: Preview - Baselibs Coverage (throwaway) -on: - push: - branches: - - feat/baselibs-integration-tests -concurrency: - # push events have no pull_request.number, so key on the ref. - group: preview-baselibs-coverage-${{ github.ref }} - cancel-in-progress: true -permissions: - contents: read -jobs: - preview: - runs-on: ${{ vars.REPO_RUNNER_LABELS && fromJSON(vars.REPO_RUNNER_LABELS) || 'ubuntu-latest' }} - steps: - - name: Clean disk space - uses: eclipse-score/more-disk-space@v1.1 - with: - level: 4 - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Set up Docker Buildx - # Required by the baselibs integration tests, which deploy the test app - # via a Docker target built from //images/linux_x86_64:image. - uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 - - name: Setup Bazel - uses: bazel-contrib/setup-bazel@0.18.0 - with: - bazelisk-cache: true - disk-cache: ${{ github.workflow }} - repository-cache: | - - MODULE.bazel - - known_good.json - - bazel_common/*.MODULE.bazel - - patches/** - - .bazelrc - cache-save: false - - name: Set up Python 3 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Install Graphviz - uses: eclipse-score/apt-install@main - with: - packages: graphviz - cache: false - - name: Execute Baselibs Integration Tests - # Must run before the documentation build and in this same workspace: - # the docs source code linker scans `bazel-testlogs` for the JUnit XML - # that carries the PartiallyVerifies properties. - run: | - bazel test --lockfile_mode=error --config=linux-x86_64 \ - //feature_integration_tests/baselibs_integration_tests:bit - - name: Show recorded PartiallyVerifies properties - if: always() - run: | - echo "## baselibs test.xml PartiallyVerifies:" - find bazel-testlogs -path '*baselibs_integration_tests*' -name 'test.xml' \ - -exec sh -c 'echo "--- $1 ---"; grep -o "feat_req__baselibs__[a-z_]*" "$1" | sort -u' _ {} \; - - name: Build documentation - run: | - bazel run \ - --lockfile_mode=error \ - //:docs_shim -- \ - --github_user=${{ github.repository_owner }} \ - --github_repo=${{ github.event.repository.name }} - - name: Verify baselibs feature requirements are linked by tests - run: | - set -e - echo "## Searching rendered docs for baselibs test links..." - missing=0 - for t in \ - test_json_library_parses_document \ - test_utils_library_base64_roundtrip \ - test_bitmanipulation_library_sets_and_toggles_bits \ - test_hash_library_crc32_ieee_check_value \ - test_containers_library_dynamic_array \ - test_result_library_error_handling \ - test_concurrency_library_notification \ - test_filesystem_library_path_decomposition \ - test_memory_library_pmr_ring_buffer \ - test_static_reflection_library_serialize_roundtrip \ - test_flatbuffers_library_build_and_lookup; do - if grep -rq "$t" _build; then - echo "OK $t is referenced in the rendered docs" - else - echo "MISS $t is NOT referenced in the rendered docs" - missing=1 - fi - done - echo "## feat_req__baselibs__* occurrences carrying a testlink in needs.json:" - grep -o "feat_req__baselibs__[a-z_]*" _build/needs.json 2>/dev/null | sort -u || true - if [ "$missing" -ne 0 ]; then - echo "::error::At least one baselibs feature test is not linked in the report" - exit 1 - fi - - name: Upload rendered documentation (report preview) - if: always() - run: tar -cf baselibs-report-preview.tar _build - - name: Upload report artifact - if: always() - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 - with: - name: baselibs-report-preview-${{ github.sha }} - path: baselibs-report-preview.tar - retention-days: 3 - if-no-files-found: error