diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 2e2835f4fe3..72568c4cfa1 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -25,7 +25,9 @@ "https://bcr.bazel.build/modules/apple_support/1.22.1/MODULE.bazel": "90bd1a660590f3ceffbdf524e37483094b29352d85317060b2327fff8f3f4458", "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442", "https://bcr.bazel.build/modules/apple_support/1.24.1/MODULE.bazel": "f46e8ddad60aef170ee92b2f3d00ef66c147ceafea68b6877cb45bd91737f5f8", - "https://bcr.bazel.build/modules/apple_support/1.24.1/source.json": "cf725267cbacc5f028ef13bb77e7f2c2e0066923a4dab1025e4a0511b1ed258a", + "https://bcr.bazel.build/modules/apple_support/2.8.0/MODULE.bazel": "c45f5176057092afba0be6c48a9ea02101666802f8be536947e099e94757f8ed", + "https://bcr.bazel.build/modules/apple_support/2.9.1/MODULE.bazel": "fb209b83ed172af587419acb3340389cfe3260669e5f1c05e1ac3974aca614e0", + "https://bcr.bazel.build/modules/apple_support/2.9.1/source.json": "247d9f86d94c546533fb01e6e18b018486f9a6dff5faccae8838ef12e659b2a7", "https://bcr.bazel.build/modules/aspect_bazel_lib/1.31.2/MODULE.bazel": "7bee702b4862612f29333590f4b658a5832d433d6f8e4395f090e8f4e85d442f", "https://bcr.bazel.build/modules/aspect_bazel_lib/1.38.0/MODULE.bazel": "6307fec451ba9962c1c969eb516ebfe1e46528f7fa92e1c9ac8646bef4cdaa3f", "https://bcr.bazel.build/modules/aspect_bazel_lib/1.42.2/MODULE.bazel": "2e0d8ab25c57a14f56ace1c8e881b69050417ff91b2fb7718dc00d201f3c3478", @@ -404,8 +406,10 @@ "https://bcr.bazel.build/modules/rules_cc/0.2.14/MODULE.bazel": "353c99ed148887ee89c54a17d4100ae7e7e436593d104b668476019023b58df8", "https://bcr.bazel.build/modules/rules_cc/0.2.16/MODULE.bazel": "9242fa89f950c6ef7702801ab53922e99c69b02310c39fb6e62b2bd30df2a1d4", "https://bcr.bazel.build/modules/rules_cc/0.2.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84", - "https://bcr.bazel.build/modules/rules_cc/0.2.22/MODULE.bazel": "94df4328edef9e44d38de5e73b037cd348e75e7ae55f4e21bf07878c41a31ebb", - "https://bcr.bazel.build/modules/rules_cc/0.2.22/source.json": "b2d6d6f9c332ce269ad75b89c6f3168d809a66173c9040210fb9bcc733ab42fa", + "https://bcr.bazel.build/modules/rules_cc/0.2.20/MODULE.bazel": "f5c07bce5ddcb99be21a0812ff5aadb439e688b7449c6542152363b2fd859c1a", + "https://bcr.bazel.build/modules/rules_cc/0.2.25/MODULE.bazel": "4a3d4f3606d3b3190be495dbc497acca552807d1d5540661ef0d1658472882e3", + "https://bcr.bazel.build/modules/rules_cc/0.2.26/MODULE.bazel": "a9f7b7f4a52747910965bddb1f58fc1fef50604ed1980ebdd16ea6f446d92249", + "https://bcr.bazel.build/modules/rules_cc/0.2.26/source.json": "bf9ff315da6055c1e374d627012cc51947a05b3560674af7d69fbedbb4fde6ef", "https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc", "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642", "https://bcr.bazel.build/modules/rules_diff/1.0.0/MODULE.bazel": "1739509d8db9a6cd7d3584822340d3dfe1f9f27e62462fbca60aa061d88741b2", @@ -610,6 +614,8 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.22.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.23.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.24.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/2.8.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/2.9.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/1.31.2/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/1.38.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/1.42.2/MODULE.bazel": "not found", @@ -885,7 +891,9 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.14/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.16/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.17/MODULE.bazel": "not found", - "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.22/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.20/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.25/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.26/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.4/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.8/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_diff/1.0.0/MODULE.bazel": "not found", @@ -1009,8 +1017,6 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/1.16.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/1.18.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_swift/2.1.1/MODULE.bazel": "not found", - "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_format_checker/0.1.1/MODULE.bazel": "1acc254faa90e9f97b79ac69af25b6c21c561f8d6079914f6352b9b20d26bd37", - "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_format_checker/0.1.1/source.json": "e3d10be878d1a007967b75a5c6d9c3ec3d47c320685904b7f31c689b3cbcdb3a", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_itf/0.6.0/MODULE.bazel": "84fe2438bde5db32cbcfca5db1194a56fd753f69e5466cf8e46aada7f209e613", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_itf/0.6.0/source.json": "59e6efe70c81dbda18631020035bcedc1d92ee86bcae5965989ee5126aa78c92", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_process/2.0.2/MODULE.bazel": "6d2b227bb6880e9f6871cc8cf94c83399e35c188424f7e9b826262de8149ed43", @@ -1541,7 +1547,7 @@ }, "@@bazel_jar_jar+//internal:non_module_deps.bzl%non_module_deps": { "general": { - "bzlTransitiveDigest": "HfYvHyph4US1Rb9jKW7AUfDbE5p9915R85CJ0c14mrk=", + "bzlTransitiveDigest": "WKp1gL89DpGSSx+oIwtE0toTaqU5pfTzmQSNhxgCEwk=", "usagesDigest": "P1uSZ4XnqOp90Mkh0A4Nrjx12Bgz/iceROnSbTsawGk=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -2169,7 +2175,7 @@ }, "@@rules_apple+//apple:apple.bzl%provisioning_profile_repository_extension": { "general": { - "bzlTransitiveDigest": "NcCruFgaVG8TOGmWY57IFfvqi/kgV8TWUht9jzdbxII=", + "bzlTransitiveDigest": "lgof385rXHmmk4IC53d7qCeKZlZunQ4c6SX3i40TjMI=", "usagesDigest": "vsJl8Rw5NL+5Ag2wdUDoTeRF/5klkXO8545Iy7U1Q08=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -11148,7 +11154,7 @@ }, "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { "general": { - "bzlTransitiveDigest": "gJFTSrkFabveIiWVE5fBJ7+WVJC4WdpWiFteWxRjbic=", + "bzlTransitiveDigest": "NRlprWw35DfgCebgqn1TtCp+nk0JTpyOzUUmi+3dlk8=", "usagesDigest": "1ieIYuafZ1pmP+ncVvISMfB3Em0hv9LCmCRwIH7gL8E=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, diff --git a/bazel_common/score_modules_target_sw.MODULE.bazel b/bazel_common/score_modules_target_sw.MODULE.bazel index 0b8024c37a6..3defe2fce08 100644 --- a/bazel_common/score_modules_target_sw.MODULE.bazel +++ b/bazel_common/score_modules_target_sw.MODULE.bazel @@ -45,10 +45,11 @@ git_override( bazel_dep(name = "score_persistency") git_override( module_name = "score_persistency", - commit = "961c0a0989755a1e0fe8de55e83c0a44e2e20b56", + commit = "2bfa2f1d76e943ccd760f30c3cf2cbf9c2abaa9a", patch_strip = 1, patches = [ "//patches/persistency:003-undev-deps-for-root-module.patch", + "//patches/persistency:004-kvs-pre-rename-compat.patch", ], remote = "https://github.com/eclipse-score/persistency.git", ) @@ -63,10 +64,9 @@ git_override( bazel_dep(name = "score_lifecycle") git_override( module_name = "score_lifecycle", - commit = "85da2168328d17a3cf53791ebf93534e5ed921fc", + commit = "2c3a919452b0f81a016c6c8783254d7d0189d0a9", patch_strip = 1, patches = [ - "//patches/lifecycle:001-migrate-baselibs-rust-targets.patch", "//patches/lifecycle:002-undev-integration-test-deps.patch", ], remote = "https://github.com/eclipse-score/lifecycle.git", diff --git a/bazel_common/score_modules_tooling.MODULE.bazel b/bazel_common/score_modules_tooling.MODULE.bazel index c0f401f18a7..996059ceb04 100644 --- a/bazel_common/score_modules_tooling.MODULE.bazel +++ b/bazel_common/score_modules_tooling.MODULE.bazel @@ -44,11 +44,7 @@ git_override( bazel_dep(name = "score_platform") git_override( module_name = "score_platform", - commit = "6d65a262c2329328cb9cbdd276bb143b417c54a4", - patch_strip = 1, - patches = [ - "//patches/platform:001-remove-duplicate-lifecycle-glossary.patch", - ], + commit = "32759e0a51e02ed87311e12d0f8125e5f8cb4de0", remote = "https://github.com/eclipse-score/score.git", ) @@ -69,14 +65,14 @@ git_override( bazel_dep(name = "score_docs_as_code") git_override( module_name = "score_docs_as_code", - commit = "f1b30375e49646d95ca52565c41f5b926b0aa884", + commit = "d9a43b1a48277bf8e9caa467aad07779ce575dec", remote = "https://github.com/eclipse-score/docs-as-code.git", ) bazel_dep(name = "score_process_description") git_override( module_name = "score_process_description", - commit = "98d1d5f42dad412a09a888ea25e59c62fa6371ce", + commit = "0e01b2ccd172fa530553dd78fc727f51c03b1337", remote = "https://github.com/eclipse-score/process_description.git", ) diff --git a/docs/verification_report/modules/index.rst b/docs/verification_report/modules/index.rst index 4daddeff330..cc7c20951c2 100644 --- a/docs/verification_report/modules/index.rst +++ b/docs/verification_report/modules/index.rst @@ -18,7 +18,7 @@ Module Verification Reports This page lists the per-module verification reports. * :doc:`Baselibs ` -* :doc:`Lifecycle ` +* :doc:`Lifecycle ` * :doc:`Logging ` * :doc:`Persistency ` @@ -27,6 +27,6 @@ This page lists the per-module verification reports. :hidden: Baselibs - Lifecycle + Lifecycle Logging Persistency diff --git a/feature_integration_tests/test_cases/tests/persistency/test_combined_requirements.py b/feature_integration_tests/test_cases/tests/persistency/test_combined_requirements.py index ecb12666948..6f2073601fd 100644 --- a/feature_integration_tests/test_cases/tests/persistency/test_combined_requirements.py +++ b/feature_integration_tests/test_cases/tests/persistency/test_combined_requirements.py @@ -36,7 +36,7 @@ # --------------------------------------------------------------------------- -# Scenario 1: Mixed value types with UTF-8 keys in one snapshot +# Scenario 1: Mixed value types in one snapshot, non-ASCII keys rejected # --------------------------------------------------------------------------- @@ -51,12 +51,14 @@ ) class TestAllTypesWithUtf8Keys(PersistencyScenario): """ - Verify that KVS can store multiple value types simultaneously under both - ASCII and UTF-8 encoded key names, and that all of them are physically - present in the single persisted snapshot file. - - This combines key-encoding support (UTF-8) with value-type coverage - (i32, f64, bool, str, null) in one observable storage outcome. + Verify that KVS can store multiple value types simultaneously under ASCII + key names, that all of them are physically present in the single persisted + snapshot file, and that keys with non-ASCII UTF-8 characters are rejected. + + KVS keys must be printable ASCII (the ASCII subset of UTF-8). The scenario + fails if a non-ASCII key is accepted. This combines key-encoding with + value-type coverage (i32, f64, bool, str, null) in one observable storage + outcome. """ @pytest.fixture(scope="class") @@ -85,11 +87,11 @@ def test_value_types_persisted(self, results: ScenarioResult, temp_dir: Path) -> assert snapshot["ascii_i32"]["t"] == "i32" assert snapshot["ascii_i32"]["v"] == -321 - assert snapshot["emoji_f64 🎯"]["t"] == "f64" - assert isclose(snapshot["emoji_f64 🎯"]["v"], 3.14, abs_tol=1e-4) + assert snapshot["ascii_f64"]["t"] == "f64" + assert isclose(snapshot["ascii_f64"]["v"], 3.14, abs_tol=1e-4) - assert snapshot["greek_bool αβγ"]["t"] == "bool" - assert snapshot["greek_bool αβγ"]["v"] is True + assert snapshot["ascii_bool"]["t"] == "bool" + assert snapshot["ascii_bool"]["v"] is True assert snapshot["ascii_str"]["t"] == "str" assert snapshot["ascii_str"]["v"] == "hello" @@ -97,6 +99,14 @@ def test_value_types_persisted(self, results: ScenarioResult, temp_dir: Path) -> assert snapshot["ascii_null"]["t"] == "null" assert snapshot["ascii_null"]["v"] is None + def test_non_ascii_keys_absent(self, results: ScenarioResult, temp_dir: Path) -> None: + """Keys with non-ASCII UTF-8 characters are rejected and never reach the snapshot.""" + assert results.return_code == ResultCode.SUCCESS + snapshot = read_kvs_snapshot(temp_dir, 1) + + assert "emoji_f64 🎯" not in snapshot + assert "greek_bool αβγ" not in snapshot + def test_snapshot_hash_matches_content(self, results: ScenarioResult, temp_dir: Path) -> None: """Verify the hash file matches the Adler-32 of the snapshot JSON after normalization.""" assert results.return_code == ResultCode.SUCCESS @@ -200,7 +210,7 @@ def test_default_values_accessible(self, results: ScenarioResult, logs_info_leve # --------------------------------------------------------------------------- -# Scenario 3: UTF-8 keys in defaults file + selective override +# Scenario 3: Non-ASCII UTF-8 keys in defaults file are rejected # --------------------------------------------------------------------------- @@ -215,23 +225,22 @@ def test_default_values_accessible(self, results: ScenarioResult, logs_info_leve ) class TestUtf8KeysWithDefaults(PersistencyScenario): """ - Verify that UTF-8 encoded key names work correctly as keys in both the - defaults file and the KVS snapshot. + Verify that a defaults file with non-ASCII UTF-8 key names is rejected. - The defaults file contains three keys with UTF-8 names (ASCII, emoji, - Greek script). The scenario overrides only the emoji key. Python verifies: - - the emoji key appears in the snapshot with the override value - - the ASCII and Greek default-only keys are absent from the snapshot + KVS keys must be printable ASCII (the ASCII subset of UTF-8), and keys read + from the defaults file are validated like keys passed to set_value. The + defaults file contains one ASCII key and two non-ASCII keys (emoji, Greek + script); the scenario fails unless opening it fails with InvalidKey. Python + verifies the scenario succeeded and that nothing was persisted. This combines key-encoding (support_datatype_keys) with default value - provisioning (default_values + default_value_file) in one storage outcome. + provisioning (default_values + default_value_file). """ _KEY_ASCII = "utf8_ascii_key" _KEY_EMOJI = "utf8_emoji 🔑" _KEY_GREEK = "utf8_greek κλμ" _DEFAULT_VALUE = 42.0 - _OVERRIDE_VALUE = 777.0 @pytest.fixture(scope="class") def scenario_name(self) -> str: @@ -239,7 +248,7 @@ def scenario_name(self) -> str: @pytest.fixture(scope="class") def defaults_file(self, temp_dir: Path) -> Path: - """Create defaults using UTF-8 key names.""" + """Create defaults using non-ASCII UTF-8 key names.""" return create_kvs_defaults_file( temp_dir, 1, @@ -262,58 +271,18 @@ def test_config(self, temp_dir: Path, defaults_file: Path) -> dict[str, Any]: }, } - def test_emoji_override_persisted(self, results: ScenarioResult, temp_dir: Path) -> None: - """ - The explicitly overridden UTF-8 emoji key must appear in the snapshot - with the override value, confirming that UTF-8 key names survive the - full round-trip through the defaults file and snapshot storage. - """ + def test_non_ascii_defaults_rejected(self, results: ScenarioResult) -> None: + """The scenario only succeeds if KVS refused to open the defaults file.""" assert results.return_code == ResultCode.SUCCESS - snapshot = read_kvs_snapshot(temp_dir, 1) - - assert self._KEY_EMOJI in snapshot, ( - f"Overridden UTF-8 emoji key '{self._KEY_EMOJI}' must be present in snapshot" - ) - assert isclose(snapshot[self._KEY_EMOJI]["v"], self._OVERRIDE_VALUE, abs_tol=1e-4) - def test_default_only_utf8_keys_absent(self, results: ScenarioResult, temp_dir: Path) -> None: - """ - UTF-8 keys that were not explicitly overridden must remain absent from - the snapshot, demonstrating that defaults do not pollute persisted storage. - """ - assert results.return_code == ResultCode.SUCCESS - snapshot = read_kvs_snapshot(temp_dir, 1) - - assert self._KEY_ASCII not in snapshot, ( - f"Default-only ASCII key '{self._KEY_ASCII}' must be absent from snapshot" - ) - assert self._KEY_GREEK not in snapshot, ( - f"Default-only Greek key '{self._KEY_GREEK}' must be absent from snapshot" - ) - - def test_snapshot_hash_matches_content(self, results: ScenarioResult, temp_dir: Path) -> None: - """Verify the hash file matches the Adler-32 of the snapshot JSON after normalization.""" - assert results.return_code == ResultCode.SUCCESS - verify_kvs_snapshot_hash(temp_dir, instance_id=1, snapshot_id=0) - - def test_utf8_default_values_accessible(self, results: ScenarioResult, logs_info_level: Any) -> None: - """ - Verify that default values behind UTF-8 ASCII and Greek keys are accessible - via get_value even though they were never explicitly written. - - Checks structured log fields emitted by the scenario after flush. - """ + def test_no_snapshot_written(self, results: ScenarioResult, temp_dir: Path) -> None: + """A rejected KVS must not persist anything.""" assert results.return_code == ResultCode.SUCCESS - log_ascii = logs_info_level.find_log("key", value="utf8_ascii_key") - assert log_ascii is not None, "Expected log entry for default utf8_ascii_key" - assert isclose(float(log_ascii.value), self._DEFAULT_VALUE, abs_tol=1e-4) - log_greek = logs_info_level.find_log("key", value="utf8_greek κλμ") - assert log_greek is not None, "Expected log entry for default utf8_greek κλμ" - assert isclose(float(log_greek.value), self._DEFAULT_VALUE, abs_tol=1e-4) + assert not (temp_dir / "kvs_1_0.json").exists() # --------------------------------------------------------------------------- -# Scenario 4: UTF-8 key in defaults file + get_value without set_value +# Scenario 4: Punctuation key in defaults file + get_value without set_value # --------------------------------------------------------------------------- @@ -325,16 +294,17 @@ def test_utf8_default_values_accessible(self, results: ScenarioResult, logs_info ) class TestUtf8DefaultValueGet(PersistencyScenario): """ - Verify that get_value retrieves the correct default for a UTF-8 emoji key - that was provisioned in the defaults file but never explicitly set. + Verify that get_value retrieves the correct default for a key made of + printable ASCII punctuation and a space (the edges of the allowed key + characters) that was provisioned in the defaults file but never explicitly set. - The scenario reads the default via the UTF-8 key, writes the result to an - ASCII probe key, and flushes. Python checks the probe key equals the expected + The scenario reads the default via that key, writes the result to a plain + probe key, and flushes. Python checks the probe key equals the expected default — combining feat_req__persistency__default_value_get with feat_req__persistency__support_datatype_keys in one storage outcome. """ - _GET_KEY = "probe 🔍" + _GET_KEY = "probe ~!#$%" _GET_DEFAULT_VALUE = 42.0 @pytest.fixture(scope="class") @@ -343,7 +313,7 @@ def scenario_name(self) -> str: @pytest.fixture(scope="class") def defaults_file(self, temp_dir: Path) -> Path: - """Provision a default value behind a UTF-8 emoji key.""" + """Provision a default value behind a punctuation key.""" return create_kvs_defaults_file( temp_dir, 1, @@ -364,8 +334,8 @@ def test_config(self, temp_dir: Path, defaults_file: Path) -> dict[str, Any]: def test_utf8_default_value_readable(self, results: ScenarioResult, temp_dir: Path) -> None: """ - The ASCII probe key written by the scenario must equal the UTF-8 key's - default, confirming that get_value works correctly with UTF-8 default keys. + The probe key written by the scenario must equal the punctuation key's + default, confirming that get_value works with such default keys. """ assert results.return_code == ResultCode.SUCCESS snapshot = read_kvs_snapshot(temp_dir, 1) diff --git a/feature_integration_tests/test_scenarios/cpp/src/internals/persistency/kvs_instance.cpp b/feature_integration_tests/test_scenarios/cpp/src/internals/persistency/kvs_instance.cpp index 703699da627..f7a0b09c540 100644 --- a/feature_integration_tests/test_scenarios/cpp/src/internals/persistency/kvs_instance.cpp +++ b/feature_integration_tests/test_scenarios/cpp/src/internals/persistency/kvs_instance.cpp @@ -186,9 +186,9 @@ std::optional> KvsInstance::create(const KvsParamet try { if (params.snapshot_max_count.has_value() && - params.snapshot_max_count.value() != static_cast(KVS_MAX_SNAPSHOTS)) { + params.snapshot_max_count.value() != static_cast(kDefaultMaxSnapshots)) { std::cerr << "Unsupported snapshot_max_count for C++ KVS API: requested " - << params.snapshot_max_count.value() << ", supported " << KVS_MAX_SNAPSHOTS + << params.snapshot_max_count.value() << ", supported " << kDefaultMaxSnapshots << std::endl; return std::nullopt; } diff --git a/feature_integration_tests/test_scenarios/cpp/src/scenarios/persistency/supported_datatypes.cpp b/feature_integration_tests/test_scenarios/cpp/src/scenarios/persistency/supported_datatypes.cpp index 3265c80fa27..4d7316ba0f0 100644 --- a/feature_integration_tests/test_scenarios/cpp/src/scenarios/persistency/supported_datatypes.cpp +++ b/feature_integration_tests/test_scenarios/cpp/src/scenarios/persistency/supported_datatypes.cpp @@ -71,9 +71,11 @@ class AllValueTypes : public Scenario { } }; -/// Write five values with mixed types under both ASCII and UTF-8 key names, -/// then flush and normalize. Python reads the single snapshot and verifies -/// every key is present with the correct type tag and value. +/// Write five values with mixed types under ASCII key names, check that keys +/// with non-ASCII UTF-8 characters are rejected (KVS keys must be printable +/// ASCII), then flush and normalize. Python reads the single snapshot and +/// verifies the ASCII keys carry the correct type tag and value and the +/// rejected keys are absent. class AllTypesUtf8 : public Scenario { public: std::string name() const final { @@ -91,11 +93,17 @@ class AllTypesUtf8 : public Scenario { }; check(kvs.set_value("ascii_i32", KvsValue(static_cast(-321))), "ascii_i32"); - check(kvs.set_value(u8"emoji_f64 🎯", KvsValue(3.14)), u8"emoji_f64 🎯"); - check(kvs.set_value(u8"greek_bool αβγ", KvsValue(true)), u8"greek_bool αβγ"); + check(kvs.set_value("ascii_f64", KvsValue(3.14)), "ascii_f64"); + check(kvs.set_value("ascii_bool", KvsValue(true)), "ascii_bool"); check(kvs.set_value("ascii_str", KvsValue("hello")), "ascii_str"); check(kvs.set_value("ascii_null", KvsValue(nullptr)), "ascii_null"); + for (const std::string key : {u8"emoji_f64 🎯", u8"greek_bool αβγ"}) { + if (kvs.set_value(key, KvsValue(3.14))) { + throw std::runtime_error("Non-ASCII key was accepted: " + key); + } + } + if (!kvs.flush()) { throw std::runtime_error("Failed to flush"); } diff --git a/feature_integration_tests/test_scenarios/cpp/src/scenarios/persistency/utf8_defaults.cpp b/feature_integration_tests/test_scenarios/cpp/src/scenarios/persistency/utf8_defaults.cpp index b75244c0947..92ede832fc5 100644 --- a/feature_integration_tests/test_scenarios/cpp/src/scenarios/persistency/utf8_defaults.cpp +++ b/feature_integration_tests/test_scenarios/cpp/src/scenarios/persistency/utf8_defaults.cpp @@ -16,15 +16,17 @@ #include +#include #include #include namespace { -/// Open a KVS whose defaults file was created with UTF-8 encoded key names. -/// Override only the emoji key and flush. Python verifies: -/// - the emoji key appears in the snapshot with the override value -/// - the ASCII and Greek keys are absent (never explicitly written) +/// Open a KVS whose defaults file contains keys with non-ASCII UTF-8 +/// characters. KVS keys must be printable ASCII, so opening must fail with +/// kInvalidKey; the scenario fails if the KVS accepts the file or fails for +/// another reason. Python verifies the scenario +/// succeeded and that no snapshot was written. /// /// Combines feat_req__persistency__support_datatype_keys, /// feat_req__persistency__default_values, and @@ -38,45 +40,22 @@ class Utf8Defaults : public Scenario { void run(const std::string& input) const override { KvsParameters params = KvsParameters::from_json_section(input, "kvs_parameters_1"); - auto kvs_opt = KvsInstance::create(params); - if (!kvs_opt) { - throw std::runtime_error("Failed to create KVS instance"); - } - auto kvs = *kvs_opt; - - // Override only the emoji key; ascii and greek keys remain as defaults. - if (!kvs->set_value(u8"utf8_emoji 🔑", 777.0)) { - throw std::runtime_error("Failed to set value"); - } - - if (!kvs->flush()) { - throw std::runtime_error("Failed to flush KVS"); - } - - // Log default values for ascii and greek keys so Python can assert they are accessible. - auto val_ascii = kvs->get_default_value_f64("utf8_ascii_key"); - if (!val_ascii.has_value()) { - throw std::runtime_error("Failed to read default value for 'utf8_ascii_key'"); - } - auto val_greek = kvs->get_default_value_f64(u8"utf8_greek κλμ"); - if (!val_greek.has_value()) { - throw std::runtime_error(u8"Failed to read default value for 'utf8_greek κλμ'"); - } - kvs_build_helpers::log_info( - "\"key\":\"utf8_ascii_key\",\"value\":" + kvs_build_helpers::format_double_python(val_ascii.value()), - "cpp_test_scenarios::scenarios::persistency::utf8_defaults"); - kvs_build_helpers::log_info( - u8"\"key\":\"utf8_greek \u03ba\u03bb\u03bc\",\"value\":" + kvs_build_helpers::format_double_python(val_greek.value()), - "cpp_test_scenarios::scenarios::persistency::utf8_defaults"); - - if (!KvsInstance::normalize_snapshot_file_to_rust_envelope(params)) { - std::cerr << "Warning: Failed to normalize snapshot file" << std::endl; + try { + kvs_build_helpers::create_kvs(params); + } catch (const std::runtime_error& e) { + // create_kvs throws with the KVS error message; kInvalidKey reads "Key is not valid". + if (std::string(e.what()) == "Key is not valid") { + return; + } + throw std::runtime_error(std::string("Expected kInvalidKey, got: ") + e.what()); } + throw std::runtime_error("KVS accepted a defaults file with non-ASCII keys"); } }; -/// Read a default value whose key is a UTF-8 emoji string without ever calling -/// set_value on it. Write the retrieved value to an ASCII result key and flush. +/// Read a default value whose key uses printable ASCII punctuation and a space +/// (the edges of the allowed key characters) without ever calling set_value on +/// it. Write the retrieved value to an ASCII result key and flush. /// Python verifies the result key equals the expected default, combining /// feat_req__persistency__default_value_get with /// feat_req__persistency__support_datatype_keys in one observable storage outcome. @@ -95,13 +74,13 @@ class Utf8DefaultValueGet : public Scenario { } auto kvs = *kvs_opt; - // Read the default via a UTF-8 emoji key — never explicitly set. - auto default_result = kvs->get_default_value_f64(u8"probe 🔍"); + // Read the default via a punctuation key — never explicitly set. + auto default_result = kvs->get_default_value_f64("probe ~!#$%"); if (!default_result.has_value()) { - throw std::runtime_error(u8"Failed to read default value for 'probe 🔍'"); + throw std::runtime_error("Failed to read default value for 'probe ~!#$%'"); } - // Persist to an ASCII result key so Python can verify without UTF-8 key lookup. + // Persist to a plain result key so Python can verify it independently. if (!kvs->set_value("result_key", default_result.value())) { throw std::runtime_error("Failed to set result_key"); } diff --git a/feature_integration_tests/test_scenarios/rust/src/scenarios/persistency/supported_datatypes.rs b/feature_integration_tests/test_scenarios/rust/src/scenarios/persistency/supported_datatypes.rs index 89dfea07f99..bcc3e0327aa 100644 --- a/feature_integration_tests/test_scenarios/rust/src/scenarios/persistency/supported_datatypes.rs +++ b/feature_integration_tests/test_scenarios/rust/src/scenarios/persistency/supported_datatypes.rs @@ -68,10 +68,12 @@ impl Scenario for AllValueTypes { } } -/// Write five values with mixed types under both ASCII and UTF-8 key names, -/// then flush. Python reads the single snapshot and verifies every key is -/// present with the correct type tag and value — combining key-encoding and -/// value-type requirements in one observable storage outcome. +/// Write five values with mixed types under ASCII key names, check that keys +/// with non-ASCII UTF-8 characters are rejected (KVS keys must be printable +/// ASCII), then flush. Python reads the single snapshot and verifies the ASCII +/// keys carry the correct type tag and value and the rejected keys are absent — +/// combining key-encoding and value-type requirements in one observable +/// storage outcome. struct AllTypesUtf8; impl Scenario for AllTypesUtf8 { @@ -86,14 +88,20 @@ impl Scenario for AllTypesUtf8 { kvs.set_value("ascii_i32", KvsValue::I32(-321)) .map_err(|e| format!("{e:?}"))?; - kvs.set_value("emoji_f64 🎯", KvsValue::F64(3.14)) + kvs.set_value("ascii_f64", KvsValue::F64(3.14)) .map_err(|e| format!("{e:?}"))?; - kvs.set_value("greek_bool αβγ", KvsValue::Boolean(true)) + kvs.set_value("ascii_bool", KvsValue::Boolean(true)) .map_err(|e| format!("{e:?}"))?; kvs.set_value("ascii_str", KvsValue::String("hello".to_string())) .map_err(|e| format!("{e:?}"))?; kvs.set_value("ascii_null", KvsValue::Null) .map_err(|e| format!("{e:?}"))?; + + for key in ["emoji_f64 🎯", "greek_bool αβγ"] { + if kvs.set_value(key, KvsValue::F64(3.14)).is_ok() { + return Err(format!("Non-ASCII key was accepted: {key}")); + } + } kvs.flush().map_err(|e| format!("{e:?}"))?; Ok(()) } diff --git a/feature_integration_tests/test_scenarios/rust/src/scenarios/persistency/utf8_defaults.rs b/feature_integration_tests/test_scenarios/rust/src/scenarios/persistency/utf8_defaults.rs index 99f17eeaa0f..345df188f8c 100644 --- a/feature_integration_tests/test_scenarios/rust/src/scenarios/persistency/utf8_defaults.rs +++ b/feature_integration_tests/test_scenarios/rust/src/scenarios/persistency/utf8_defaults.rs @@ -14,20 +14,20 @@ use crate::internals::persistency::{ kvs_instance::{get_default_value_f64, kvs_instance}, kvs_parameters::KvsParameters, }; -use rust_kvs::prelude::KvsApi; +use rust_kvs::prelude::{ErrorCode, KvsApi}; use serde_json::Value; use test_scenarios_rust::scenario::Scenario; -use tracing::info; fn parse_params(input: &str) -> Result { let v: Value = serde_json::from_str(input).map_err(|e| e.to_string())?; KvsParameters::from_value(&v["kvs_parameters_1"]).map_err(|e| e.to_string()) } -/// Open a KVS whose defaults file was created with UTF-8 encoded key names. -/// Override only the emoji key and flush. Python verifies: -/// - the emoji key appears in the snapshot with the override value -/// - the ASCII and Greek keys are absent from the snapshot (never explicitly written) +/// Open a KVS whose defaults file contains keys with non-ASCII UTF-8 +/// characters. KVS keys must be printable ASCII, so opening must fail with +/// InvalidKey; the scenario fails if the KVS accepts the file or fails for +/// another reason. Python verifies the scenario +/// succeeded and that no snapshot was written. /// /// Combines feat_req__persistency__support_datatype_keys, /// feat_req__persistency__default_values, and @@ -41,25 +41,17 @@ impl Scenario for Utf8Defaults { fn run(&self, input: &str) -> Result<(), String> { let params = parse_params(input)?; - let kvs = kvs_instance(params).map_err(|e| format!("{e:?}"))?; - - // Override only the emoji key; ASCII and Greek keys are intentionally left as defaults. - kvs.set_value("utf8_emoji 🔑", 777.0_f64) - .map_err(|e| format!("{e:?}"))?; - kvs.flush().map_err(|e| format!("{e:?}"))?; - // Log default values for ASCII and Greek keys so Python can assert accessibility. - let val_ascii: f64 = get_default_value_f64(&kvs, "utf8_ascii_key") - .map_err(|e| format!("Failed to read default utf8_ascii_key: {e:?}"))?; - let val_greek: f64 = get_default_value_f64(&kvs, "utf8_greek κλμ") - .map_err(|e| format!("Failed to read default utf8_greek κλμ: {e:?}"))?; - info!(key = "utf8_ascii_key", value = val_ascii, source = "default"); - info!(key = "utf8_greek κλμ", value = val_greek, source = "default"); - Ok(()) + match kvs_instance(params) { + Ok(_) => Err("KVS accepted a defaults file with non-ASCII keys".to_string()), + Err(ErrorCode::InvalidKey) => Ok(()), + Err(e) => Err(format!("Expected InvalidKey, got {e:?}")), + } } } -/// Read a default value whose key is a UTF-8 emoji string without ever calling -/// set_value on it. Write the retrieved value to an ASCII result key and flush. +/// Read a default value whose key uses printable ASCII punctuation and a space +/// (the edges of the allowed key characters) without ever calling set_value on +/// it. Write the retrieved value to an ASCII result key and flush. /// Python verifies the result key equals the expected default, combining /// feat_req__persistency__default_value_get with /// feat_req__persistency__support_datatype_keys in one observable storage outcome. @@ -73,10 +65,10 @@ impl Scenario for Utf8DefaultValueGet { fn run(&self, input: &str) -> Result<(), String> { let params = parse_params(input)?; let kvs = kvs_instance(params).map_err(|e| format!("{e:?}"))?; - // Read the default value via a UTF-8 emoji key — never explicitly set. - let default_val: f64 = get_default_value_f64(&kvs, "probe 🔍") - .map_err(|e| format!("Failed to read UTF-8 default value: {e:?}"))?; - // Persist to an ASCII result key so Python can verify without UTF-8 key lookup. + // Read the default value via a punctuation key — never explicitly set. + let default_val: f64 = + get_default_value_f64(&kvs, "probe ~!#$%").map_err(|e| format!("Failed to read default value: {e:?}"))?; + // Persist to a plain result key so Python can verify it independently. kvs.set_value("result_key", default_val).map_err(|e| format!("{e:?}"))?; kvs.flush().map_err(|e| format!("{e:?}"))?; Ok(()) diff --git a/known_good.json b/known_good.json index 9f9be872721..12188ec0f4d 100644 --- a/known_good.json +++ b/known_good.json @@ -85,7 +85,8 @@ "score_persistency": { "repo": "https://github.com/eclipse-score/persistency.git", "bazel_patches": [ - "//patches/persistency:003-undev-deps-for-root-module.patch" + "//patches/persistency:003-undev-deps-for-root-module.patch", + "//patches/persistency:004-kvs-pre-rename-compat.patch" ], "metadata": { "code_root_path": "//score/kvs/...", @@ -112,7 +113,7 @@ "//score/kvs/rust_kvs:tests" ] }, - "hash": "961c0a0989755a1e0fe8de55e83c0a44e2e20b56" + "hash": "2bfa2f1d76e943ccd760f30c3cf2cbf9c2abaa9a" }, "score_kyron": { "repo": "https://github.com/eclipse-score/kyron.git", @@ -133,7 +134,6 @@ "score_lifecycle": { "repo": "https://github.com/eclipse-score/lifecycle.git", "bazel_patches": [ - "//patches/lifecycle:001-migrate-baselibs-rust-targets.patch", "//patches/lifecycle:002-undev-integration-test-deps.patch" ], "metadata": { @@ -163,7 +163,7 @@ "//..." ] }, - "hash": "85da2168328d17a3cf53791ebf93534e5ed921fc" + "hash": "2c3a919452b0f81a016c6c8783254d7d0189d0a9" }, "score_logging": { "repo": "https://github.com/eclipse-score/logging.git", @@ -290,10 +290,7 @@ }, "score_platform": { "repo": "https://github.com/eclipse-score/score.git", - "hash": "6d65a262c2329328cb9cbdd276bb143b417c54a4", - "bazel_patches": [ - "//patches/platform:001-remove-duplicate-lifecycle-glossary.patch" - ] + "hash": "32759e0a51e02ed87311e12d0f8125e5f8cb4de0" }, "score_bazel_platforms": { "repo": "https://github.com/eclipse-score/bazel_platforms.git", @@ -307,11 +304,11 @@ }, "score_docs_as_code": { "repo": "https://github.com/eclipse-score/docs-as-code.git", - "hash": "f1b30375e49646d95ca52565c41f5b926b0aa884" + "hash": "d9a43b1a48277bf8e9caa467aad07779ce575dec" }, "score_process_description": { "repo": "https://github.com/eclipse-score/process_description.git", - "hash": "98d1d5f42dad412a09a888ea25e59c62fa6371ce" + "hash": "0e01b2ccd172fa530553dd78fc727f51c03b1337" }, "score_bazel_cpp_toolchains": { "repo": "https://github.com/eclipse-score/bazel_cpp_toolchains.git", diff --git a/patches/lifecycle/001-migrate-baselibs-rust-targets.patch b/patches/lifecycle/001-migrate-baselibs-rust-targets.patch deleted file mode 100644 index fabadf7d25a..00000000000 --- a/patches/lifecycle/001-migrate-baselibs-rust-targets.patch +++ /dev/null @@ -1,92 +0,0 @@ -diff --git a/examples/rust_supervised_app/BUILD b/examples/rust_supervised_app/BUILD -index 9738ceb0b7..2dff53f79c 100644 ---- a/examples/rust_supervised_app/BUILD -+++ b/examples/rust_supervised_app/BUILD -@@ -36,8 +36,8 @@ rust_binary( - deps = [ - "//score/health_monitor:health_monitoring_rust", - "//score/launch_manager:lifecycle_rust", -- "@score_baselibs//src/log/score_log", -- "@score_baselibs//src/log/stdout_logger", -+ "@score_baselibs//score/log_rust/score_log", -+ "@score_baselibs//score/log_rust/stdout_logger", - "@score_crates//:clap", - "@score_crates//:libc", - "@score_crates//:signal_hook", -diff --git a/score/health_monitor/src/rust/BUILD b/score/health_monitor/src/rust/BUILD -index 44ab61c9e2..15b09001f1 100644 ---- a/score/health_monitor/src/rust/BUILD -+++ b/score/health_monitor/src/rust/BUILD -@@ -14,14 +14,14 @@ - load("@rules_rust//rust:defs.bzl", "miri_test", "rust_library", "rust_static_library", "rust_test") - - COMMON_RUST_DEPS = [ -- "@score_baselibs//src/containers:containers", -- "@score_baselibs//src/thread:thread", -- "@score_baselibs//src/log/score_log:score_log", -+ "@score_baselibs//score/containers_rust:containers", -+ "@score_baselibs//score/thread", -+ "@score_baselibs//score/log_rust/score_log", - "//score/launch_manager:alive_rust", - ] - - PROC_MACRO_DEPS = [ -- "@score_baselibs//src/testing_macros:score_testing_macros", -+ "@score_baselibs//score/testing_macros:score_testing_macros", - ] - - rust_library( -@@ -78,7 +78,7 @@ rust_test( - "//config:unit_host": ["@platforms//os:linux"], # see https://github.com/eclipse-score/lifecycle/issues/272 - "//conditions:default": ["@platforms//:incompatible"], - }), -- deps = ["@score_baselibs//src/log/stdout_logger"], -+ deps = ["@score_baselibs//score/log_rust/stdout_logger"], - ) - - rust_test( -@@ -110,7 +110,7 @@ rust_test( - "//conditions:default": ["@platforms//:incompatible"], - }), - deps = [ -- "@score_baselibs//src/log/stdout_logger", -+ "@score_baselibs//score/log_rust/stdout_logger", - "@score_crates//:loom", - ], - ) -diff --git a/examples/cpp_supervised_app/BUILD b/examples/cpp_supervised_app/BUILD -index 984f359388..320e13240d 100644 ---- a/examples/cpp_supervised_app/BUILD -+++ b/examples/cpp_supervised_app/BUILD -@@ -34,6 +34,6 @@ cc_binary( - deps = [ - "//score/health_monitor:health_monitoring_cc", - "//score/launch_manager:lifecycle_cc", -- "@score_baselibs//src/log/stdout_logger_cpp_init", -+ "@score_baselibs//score/log_rust/stdout_logger_cpp_init", - ], - ) -diff --git a/score/health_monitor/src/cpp/details/BUILD b/score/health_monitor/src/cpp/details/BUILD -index 6dbd9e45fa..7ccfa29283 100644 ---- a/score/health_monitor/src/cpp/details/BUILD -+++ b/score/health_monitor/src/cpp/details/BUILD -@@ -19,6 +19,6 @@ cc_library( - visibility = ["//score/health_monitor/src/cpp:__pkg__"], - deps = [ - "@googletest//:gtest", -- "@score_baselibs//src/log/stdout_logger_cpp_init", -+ "@score_baselibs//score/log_rust/stdout_logger_cpp_init", - ], - ) -diff --git a/tests/integration/complex_monitoring/BUILD b/tests/integration/complex_monitoring/BUILD -index 15306f47ec..7467efa724 100644 ---- a/tests/integration/complex_monitoring/BUILD -+++ b/tests/integration/complex_monitoring/BUILD -@@ -42,6 +42,6 @@ cc_binary( - "//score/launch_manager:lifecycle_cc", - "//tests/utils/test_helper", - "@googletest//:gtest_main", -- "@score_baselibs//src/log/stdout_logger_cpp_init", -+ "@score_baselibs//score/log_rust/stdout_logger_cpp_init", - ], - ) diff --git a/patches/lifecycle/002-undev-integration-test-deps.patch b/patches/lifecycle/002-undev-integration-test-deps.patch index 532eb7fc2dd..6abe126df8d 100644 --- a/patches/lifecycle/002-undev-integration-test-deps.patch +++ b/patches/lifecycle/002-undev-integration-test-deps.patch @@ -1,13 +1,10 @@ diff --git a/MODULE.bazel b/MODULE.bazel -index 721aacc9e..f7726c220 100644 +index 16e791f..039e76e 100644 --- a/MODULE.bazel +++ b/MODULE.bazel -@@ -174,9 +174,9 @@ pip.parse( - ) - use_repo(pip, "score_lifecycle_pip") +@@ -303,7 +303,7 @@ use_repo(pip, "codeql_coding_standards_pip_hub") --bazel_dep(name = "score_itf", version = "0.5.0", dev_dependency = True) -+bazel_dep(name = "score_itf", version = "0.5.0") + bazel_dep(name = "score_itf", version = "0.6.0") -oci = use_extension("@rules_oci//oci:extensions.bzl", "oci", dev_dependency = True) +oci = use_extension("@rules_oci//oci:extensions.bzl", "oci") diff --git a/patches/persistency/003-undev-deps-for-root-module.patch b/patches/persistency/003-undev-deps-for-root-module.patch index d2d798e4e91..c046c4c9900 100644 --- a/patches/persistency/003-undev-deps-for-root-module.patch +++ b/patches/persistency/003-undev-deps-for-root-module.patch @@ -1,26 +1,27 @@ diff --git a/MODULE.bazel b/MODULE.bazel -index 2649521..6c17327 100644 +index 2d52c32..163c13b 100644 --- a/MODULE.bazel +++ b/MODULE.bazel -@@ -38,13 +38,13 @@ bazel_dep(name = "score_coverage", version = "0.3.1", dev_dependency = True) - bazel_dep(name = "aspect_rules_lint", version = "2.7.2", dev_dependency = True) +@@ -47,14 +47,14 @@ bazel_dep(name = "score_coverage", version = "0.4.2", dev_dependency = True) + # Bump once score_rust_policies supports aspect_rules_lint >= 2.6. + bazel_dep(name = "aspect_rules_lint", version = "2.5.2", dev_dependency = True) bazel_dep(name = "buildifier_prebuilt", version = "8.5.1.3", dev_dependency = True) - bazel_dep(name = "google_benchmark", version = "1.9.5", dev_dependency = True) --bazel_dep(name = "googletest", version = "1.17.0.bcr.2", dev_dependency = True) -+bazel_dep(name = "googletest", version = "1.17.0.bcr.2") - bazel_dep(name = "score_bazel_cpp_toolchains", version = "1.0.3", dev_dependency = True) +-bazel_dep(name = "googletest", version = "1.18.0", dev_dependency = True) ++bazel_dep(name = "googletest", version = "1.18.0") + bazel_dep(name = "score_bazel_cpp_toolchains", version = "1.0.5", dev_dependency = True) + bazel_dep(name = "score_cpp_policies", version = "0.1.2", dev_dependency = True) -bazel_dep(name = "score_sbom", version = "0.1.1", dev_dependency = True) -bazel_dep(name = "score_rules_imagefs", version = "0.1.0", dev_dependency = True) +bazel_dep(name = "score_sbom", version = "0.1.1") +bazel_dep(name = "score_rules_imagefs", version = "0.1.0") bazel_dep(name = "score_rust_policies", version = "0.0.5", dev_dependency = True) bazel_dep(name = "score_toolchains_rust", version = "0.10.0", dev_dependency = True) --bazel_dep(name = "score_tools", version = "0.0.3", dev_dependency = True) -+bazel_dep(name = "score_tools", version = "0.0.3") +-bazel_dep(name = "score_tools", version = "0.0.4", dev_dependency = True) ++bazel_dep(name = "score_tools", version = "0.0.4") - # Pinned past v0.0.3 for the `$(locations)` fix in score_pytest. - git_override( -@@ -62,7 +62,7 @@ python.toolchain( + # Python toolchain configuration + PYTHON_VERSION = "3.12" +@@ -65,7 +65,7 @@ python.toolchain( python_version = PYTHON_VERSION, ) diff --git a/patches/persistency/004-kvs-pre-rename-compat.patch b/patches/persistency/004-kvs-pre-rename-compat.patch new file mode 100644 index 00000000000..ca54123021e --- /dev/null +++ b/patches/persistency/004-kvs-pre-rename-compat.patch @@ -0,0 +1,39 @@ +diff --git a/score/kvs/BUILD b/score/kvs/BUILD +index 33bfbc7..4acaed6 100644 +--- a/score/kvs/BUILD ++++ b/score/kvs/BUILD +@@ -48,6 +48,9 @@ cc_library( + "kvsbuilder.hpp", + "kvsvalue.hpp", + ], ++ # Keep the pre-rename flat include path ("kvs.hpp", "kvsbuilder.hpp", ...) ++ # for consumers that have not migrated yet, e.g. score_config_management. ++ includes = ["."], + visibility = [ + "//visibility:public", + ], +diff --git a/score/kvs/kvs_builder.h b/score/kvs/kvs_builder.h +index 4ceb714..5497adf 100644 +--- a/score/kvs/kvs_builder.h ++++ b/score/kvs/kvs_builder.h +@@ -133,6 +133,20 @@ class KvsBuilder final + return KvsLoad(flag); + } + ++ /// @deprecated Pre-rename bool form: true maps to kRequired, false to kOptional. ++ [[deprecated("Use Defaults() instead. This name is removed in a future release.")]] ++ KvsBuilder& need_defaults_flag(const bool flag) noexcept ++ { ++ return Defaults(flag ? KvsDefaults::kRequired : KvsDefaults::kOptional); ++ } ++ ++ /// @deprecated Pre-rename bool form: true maps to kRequired, false to kOptional. ++ [[deprecated("Use KvsLoad() instead. This name is removed in a future release.")]] ++ KvsBuilder& need_kvs_flag(const bool flag) noexcept ++ { ++ return KvsLoad(flag ? score::kvs::KvsLoad::kRequired : score::kvs::KvsLoad::kOptional); ++ } ++ + /// @deprecated Use `WorkingDir()` instead. + [[deprecated("Use WorkingDir() instead. This name is removed in a future release.")]] + KvsBuilder& dir(const std::string& dir_path) noexcept diff --git a/patches/platform/001-remove-duplicate-lifecycle-glossary.patch b/patches/platform/001-remove-duplicate-lifecycle-glossary.patch deleted file mode 100644 index 19c9f57f16f..00000000000 --- a/patches/platform/001-remove-duplicate-lifecycle-glossary.patch +++ /dev/null @@ -1,132 +0,0 @@ -diff --git a/docs/features/lifecycle/glossary.rst b/docs/features/lifecycle/glossary.rst ---- a/docs/features/lifecycle/glossary.rst -+++ b/docs/features/lifecycle/glossary.rst -@@ -15,123 +15,8 @@ - Lifecycle Glossary - ================== - --.. glossary:: -- Lifecycle Feature -- Feature providing support for starting and stopping processes. -- -- Launch Manager -- Component to start and stop processes on a POSIX like operating system. -- -- Health Monitor -- Provides process local monitoring functionalities such as deadline monitoring -- and logical program flow monitoring. -- -- Control Interface -- Interface to control the lifecycle of the system, e.g. to start and stop processes. -- -- Alive Interface -- Interface to monitor the aliveness of a process. -- -- Sandbox -- A sandbox is a set of configurations, which are applied to a process when it -- is started. It can include environment variables, secpol policies, cgroup -- configurations, user and group IDs etc. -- -- Health Monitor Interface -- Interface to monitor the health of a process, e.g. to check if a process is -- alive or if it is running as expected. -- -- Alive Monitoring -- Checks if an application reports an alive state in a certain period. -- -- Deadline Monitoring -- Monitor to detect if a state was reached within a specified time related to -- another event. -- -- Logical Programflow Monitoring -- Monitor to detect if certain states were reached in a defined sequence and -- time interval. -- -- Run State -- An Run State defines a set of components, which are in status running -- and are supervised by the software platform. If the health management system -- detects abnormal situation it can change the :term:`Run State`. -- -- Component -- A configurable unit in the Launch Manager that describes an executable and its runtime environment (sandbox). Components can be grouped together in Run Targets to define system operational states. -- -- Ready State -- A state when the component is ready to provide services to other components. -- -- Dependency (between components) -- A configuration parameter indicating that **Component A** can only start -- after **Component B** has reached its :term:`Ready State`. In this case, -- **Component A** depends on **Component B**. -- -- Dependency (between run targets) -- A configuration parameter indicating that **Run Target A** includes all -- components from **Run Target B**. In this case, **Run Target A** depends -- on **Run Target B**. -- -- Lifecycle Component -- Node of the dependency tree. -- -- Process -- Instantiation of an Executable programs running on the system that are managed by the Launch Manager. -- -- UID -- User Identifier - a unique number assigned to each user on a Unix-like operating system. -- -- GID -- Group Identifier - a unique number assigned to each group on a Unix-like operating system. -- -- Polling Interval -- The time interval between successive checks of a condition or status. -- -- Working Directory -- The current directory from which a process is executed, also known as CWD (Current Working Directory). -- -- File Descriptor -- A handle used by a process to access files or other input/output resources. -- -- Procmgr -- Process Manager - a QNX system component that manages process creation and execution. -- -- ASLR -- Address Space Layout Randomization - a security technique that randomizes the memory layout of processes. -- -- Recovery Action -- Actions taken by the Launch Manager when a process fails or terminates abnormally. -- -- Ready Condition -- A configurable condition that must be satisfied before a component is considered ready and operational. Ready conditions can include file system checks, network availability, or custom application-specific signals. -- -- Ready conditions can either be reported by the component itself through the Lifecycle Interface or determined via external state monitoring. External state examples include: process started, file is available, socket was opened, or that the process finished successfully. -- -- Liveliness -- The state indicating that a process is active and responding as expected. -- -- Watchdog -- A monitoring mechanism that detects system failures and can trigger recovery actions. -- -- Interval -- A period of time between events or measurements. -- -- QNX -- A real-time operating system commonly used in embedded systems. -- -- -- DSS -- Device Safe State - a safe operational state that a system can enter during failures. -- -- DAG -- Directed Acyclic Graph - a data structure used to represent dependencies between processes. -- -- SWC -- Software Components - modular software units that can be independently managed. -- -- Run target -- A named collection of processes and their dependencies that can be launched, stopped, or switched as a group to achieve a specific operational mode or configuration. -- -- Operating System -- The system software that manages computer hardware and software resources and provides common services for computer programs. -+.. note:: -+ This glossary previously duplicated content now maintained in the -+ ``score_lifecycle`` repository (`eclipse-score/lifecycle -+ `_). See its own -+ ``docs/features/lifecycle/glossary.rst`` for the current definitions. diff --git a/patches/platform/BUILD b/patches/platform/BUILD deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/platform_integration_tests/lifecycle/test_lifecycle_showcase.py b/platform_integration_tests/lifecycle/test_lifecycle_showcase.py index e6afecb47ce..c1f8769e94b 100644 --- a/platform_integration_tests/lifecycle/test_lifecycle_showcase.py +++ b/platform_integration_tests/lifecycle/test_lifecycle_showcase.py @@ -22,7 +22,7 @@ @add_test_properties( - partially_verifies=["stkh_req__dependability__safety_features_1"], + partially_verifies=["stkh_req__dependability__health_management"], test_type="interface-test", derivation_technique="requirements-analysis", ) @@ -31,4 +31,13 @@ def test_lifecycle_showcase(target): a malfunction, demonstrating the platform's health and lifecycle management.""" - run_showcases(target, "Simple Health and lifecycle management example") + output = run_showcases(target, "Simple Health and lifecycle management example") + + # The CLI exits 0 even if LM keeps restarting a crashing app, so check LM's log too. + assert "unexpected termination" not in output, output + for marker in ( + "Successfully activated Run Target Startup", + "Completed the request for Run Target fallback_run_target", + "Launch Manager completed with exit code value: 0", + ): + assert marker in output, f"Missing '{marker}' in lifecycle showcase output:\n{output}" diff --git a/platform_integration_tests/showcase_runner.py b/platform_integration_tests/showcase_runner.py index 9b8ced60119..88f23194dd7 100644 --- a/platform_integration_tests/showcase_runner.py +++ b/platform_integration_tests/showcase_runner.py @@ -19,13 +19,13 @@ _CLI = "/showcases/bin/cli" -def run_showcases(target, *names: str) -> None: +def run_showcases(target, *names: str) -> str: """Run the named showcases via the showcase entrypoint and assert success. ``run_score`` in the showcase CLI prints a per-example completion banner and exits 0 once every app of an example has been spawned and joined, so both the exit code and the banner are checked to confirm the showcase ran - end-to-end on the platform. + end-to-end on the platform. Returns the CLI output for further checks. """ selection = ",".join(names) exit_code, out = target.execute(f"{_CLI} --examples='{selection}'") @@ -36,3 +36,4 @@ def run_showcases(target, *names: str) -> None: assert f"Example '{name}' finished successfully" in output, ( f"Showcase '{name}' did not finish successfully:\n{output}" ) + return output diff --git a/showcases/simple_lifecycle/BUILD b/showcases/simple_lifecycle/BUILD index 37afd26f68f..7750de296d2 100644 --- a/showcases/simple_lifecycle/BUILD +++ b/showcases/simple_lifecycle/BUILD @@ -35,5 +35,6 @@ score_pkg_bundle( custom_layout = { ":lifecycle_configs": "etc/", "//showcases/simple_lifecycle/configs:logging.json": "etc/logging.json", + "//showcases/simple_lifecycle/configs:mw_com_config.json": "etc/mw_com_config.json", }, ) diff --git a/showcases/simple_lifecycle/configs/BUILD b/showcases/simple_lifecycle/configs/BUILD index 802ad609605..7bdb0d69052 100644 --- a/showcases/simple_lifecycle/configs/BUILD +++ b/showcases/simple_lifecycle/configs/BUILD @@ -13,4 +13,5 @@ exports_files([ "launch_manager_config.json", "logging.json", + "mw_com_config.json", ]) diff --git a/showcases/simple_lifecycle/configs/launch_manager_config.json b/showcases/simple_lifecycle/configs/launch_manager_config.json index 1f9e1b87539..f54f2123fb5 100644 --- a/showcases/simple_lifecycle/configs/launch_manager_config.json +++ b/showcases/simple_lifecycle/configs/launch_manager_config.json @@ -3,12 +3,10 @@ "defaults": { "deployment_config": { "bin_dir": "/showcases/bin/", - "ready_recovery_action": { - "restart": { - "number_of_attempts": 1, - "delay_before_restart": 0.5 - } - }, + "working_dir": "/showcases/data/simple_lifecycle", + "ready_timeout_ms": 2000, + "activation_retry_attempts": 1, + "activation_retry_delay_ms": 500, "sandbox": { "uid": 0, "gid": 0, @@ -22,7 +20,7 @@ "application_type": "Reporting", "is_self_terminating": false, "alive_supervision": { - "reporting_cycle": 0.1, + "reporting_cycle_ms": 100, "min_indications": 1, "max_indications": 3, "failed_cycles_tolerance": 1 @@ -35,7 +33,7 @@ } }, "run_target": { - "transition_timeout": 5, + "transition_timeout_ms": 5000, "recovery_action": { "switch_run_target": { "run_target": "fallback_run_target" @@ -43,7 +41,7 @@ } }, "alive_supervision" : { - "evaluation_cycle": 0.5 + "evaluation_cycle_ms": 500 } }, "components": { @@ -112,9 +110,9 @@ "fallback_run_target": { "description": "Switching off everything", "depends_on": ["state_manager"], - "transition_timeout": 1.5 + "transition_timeout_ms": 1500 }, "alive_supervision" : { - "evaluation_cycle": 0.5 + "evaluation_cycle_ms": 500 } } \ No newline at end of file diff --git a/showcases/simple_lifecycle/configs/mw_com_config.json b/showcases/simple_lifecycle/configs/mw_com_config.json new file mode 100644 index 00000000000..a478be5d746 --- /dev/null +++ b/showcases/simple_lifecycle/configs/mw_com_config.json @@ -0,0 +1,100 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/score/mw/lifecycle/LmControlService", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 7101, + "events": [ + { + "eventName": "ActivationResult", + "eventId": 1 + } + ], + "methods": [ + { + "methodName": "ActivateRunTarget", + "methodId": 2 + }, + { + "methodName": "GetActiveRunTarget", + "methodId": 3 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "LaunchManager/StateManager/Instance", + "serviceTypeName": "/score/mw/lifecycle/LmControlService", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "QM", + "binding": "SHM", + "events": [ + { + "eventName": "ActivationResult", + "numberOfSampleSlots": 8, + "maxSubscribers": 1 + } + ], + "methods": [ + { + "methodName": "ActivateRunTarget", + "queueSize": 1 + }, + { + "methodName": "GetActiveRunTarget", + "queueSize": 1 + } + ] + } + ] + }, + { + "instanceSpecifier": "StateManager/LaunchManager/Instance", + "serviceTypeName": "/score/mw/lifecycle/LmControlService", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "QM", + "binding": "SHM", + "events": [ + { + "eventName": "ActivationResult" + } + ], + "methods": [ + { + "methodName": "ActivateRunTarget", + "queueSize": 1 + }, + { + "methodName": "GetActiveRunTarget", + "queueSize": 1 + } + ] + } + ] + } + ], + "global": { + "asil-level": "QM" + } +}