diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/test_and_docs.yml index ea7d1f21c5c..826a4ebb470 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -52,6 +52,9 @@ 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. + uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 - name: Setup Bazel uses: bazel-contrib/setup-bazel@0.18.0 with: @@ -99,6 +102,12 @@ jobs: - name: Execute Feature Integration Tests run: | bazel test --config=linux-x86_64 //feature_integration_tests/test_cases:fit + - name: Execute Baselibs Integration Tests + # 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) 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..78907dc3baf --- /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 = "test_app_runner", + srcs = ["test_app_runner.py"], + # Lets the test `import test_app_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 = [ + ":test_app_runner", + ], +) diff --git a/feature_integration_tests/baselibs_integration_tests/test_app/BUILD b/feature_integration_tests/baselibs_integration_tests/test_app/BUILD new file mode 100644 index 00000000000..7185e9623e3 --- /dev/null +++ b/feature_integration_tests/baselibs_integration_tests/test_app/BUILD @@ -0,0 +1,49 @@ +# ******************************************************************************* +# 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") +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", + ":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 new file mode 100644 index 00000000000..401182cde1d --- /dev/null +++ b/feature_integration_tests/baselibs_integration_tests/test_app/main.cpp @@ -0,0 +1,350 @@ +/******************************************************************************** + * 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 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 +// 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/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 +{ + +// Minimal self-contained error domain so the result subcommand can exercise the +// error path without pulling in a logging backend. +enum class AppErrc : score::result::ErrorCode +{ + kDivideByZero = 1, +}; + +class AppErrorDomain final : public score::result::ErrorDomain +{ + public: + std::string_view MessageFor(const score::result::ErrorCode& code) const noexcept override + { + return (static_cast(code) == AppErrc::kDivideByZero) ? "division by zero" : "unknown error"; + } +}; + +constexpr AppErrorDomain kAppErrorDomain; + +score::result::Error MakeError(const AppErrc code, const std::string_view user_message = "") noexcept +{ + 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(AppErrc::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, feat_req__baselibs__panic_free_development +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; +} + +// 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"; + 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(); + } + 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_app_runner.py b/feature_integration_tests/baselibs_integration_tests/test_app_runner.py new file mode 100644 index 00000000000..a7bd1333e57 --- /dev/null +++ b/feature_integration_tests/baselibs_integration_tests/test_app_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 test app on the deployed target.""" + +import logging + +logger = logging.getLogger(__name__) + +_TEST_APP_BIN = "/usr/bin/baselibs_test_app" + + +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"{_TEST_APP_BIN} {subcommand}") + output = out.decode(errors="replace").strip() + 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 new file mode 100644 index 00000000000..bf527d56d1d --- /dev/null +++ b/feature_integration_tests/baselibs_integration_tests/test_baselibs_features.py @@ -0,0 +1,193 @@ +# ******************************************************************************* +# 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_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 +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 test_app_runner import run_test_app + + +@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_test_app(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_test_app(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_test_app(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_test_app(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", + "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. + + 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", + "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. + + 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 diff --git a/images/linux_x86_64/BUILD b/images/linux_x86_64/BUILD index 3eee222f6d6..66d2c2e29ed 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_test_app_tar", + srcs = ["//feature_integration_tests/baselibs_integration_tests/test_app:baselibs_test_app"], + 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_test_app_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__", ], )