diff --git a/tools/metamodel/common/source_location.rs b/tools/metamodel/common/source_location.rs index d1b42936..b34ac7ac 100644 --- a/tools/metamodel/common/source_location.rs +++ b/tools/metamodel/common/source_location.rs @@ -39,6 +39,13 @@ impl SourceLocation { return normalize_source_path(relative.to_string_lossy().as_ref()); } } + + // TODO: Do not store that absolute sandbox path as the source + // location. Source locations should be stable across actions, for + // example repo-relative. + if let Some(relative) = strip_bazel_execroot_prefix(self.file.as_ref()) { + return relative; + } } normalize_source_path(self.file.as_ref()) @@ -53,6 +60,11 @@ fn normalize_source_path(path: &str) -> String { path.strip_prefix("./").unwrap_or(path).to_string() } +fn strip_bazel_execroot_prefix(path: &str) -> Option { + let (_, relative) = path.split_once("/execroot/_main/")?; + Some(normalize_source_path(relative)) +} + impl fmt::Display for SourceLocation { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.display_file(), self.line) diff --git a/validation/core/BUILD b/validation/core/BUILD index 415b5636..3a21914b 100644 --- a/validation/core/BUILD +++ b/validation/core/BUILD @@ -53,6 +53,7 @@ rust_library( "src/readers/mod.rs", "src/readers/sequence_diagram_reader.rs", "src/results/diagnostics.rs", + "src/results/error_models.rs", "src/results/mod.rs", "src/validators/bazel_component_validator.rs", "src/validators/class_design_implementation_validator.rs", @@ -74,6 +75,7 @@ rust_library( visibility = ["//visibility:public"], deps = [ "//tools/metamodel/class:class_diagram", + "//tools/metamodel/common:source_location", "//tools/metamodel/component:component_diagram", "//tools/metamodel/sequence:sequence_diagram", "//tools/serialization/flatbuffers/class:class_fbs", diff --git a/validation/core/integration_test/README.md b/validation/core/integration_test/README.md index 0b464034..156aedfd 100644 --- a/validation/core/integration_test/README.md +++ b/validation/core/integration_test/README.md @@ -72,7 +72,7 @@ produced in layer 1. No copying or re-parsing occurs; the underlying Bazel action cache entry is reused. A `filegroup` named `case_data` then bundles the `fbs` target together with the -static fixture files (`architecture.json`, `expected.json` or `expected.yaml`), +static fixture files (`architecture.json` and `expected.yaml`), making the whole case available as a single Bazel dependency. For `ComponentInternalApi` and `SequenceInternalApi` suites, cases include @@ -96,7 +96,6 @@ The shared `test_framework` library provides the following helpers: | Helper | Description | |--------|-------------| | `collect_case_fbs_files(suite, case, category)` | Returns sorted absolute paths to every `.fbs.bin` in a category subdirectory | -| `load_expected_fixture(suite, case)` | Deserializes `expected.json` into `ExpectedFixture` | | `load_expected_yaml_fixture(suite, case)` | Deserializes `expected.yaml` into `ExpectedFixture` | | `run_validation_profile(case_name, profile, input_bundle)` | Writes a profile-owned input bundle, spawns the CLI binary, and returns `CliRunResult` | | `assert_cli_result(case, expected, result)` | Asserts exit code and checks each string in `error_contains` against the log | @@ -120,7 +119,7 @@ design/component_diagram.fbs.bin ← in ArchitecturalDesignInfo.static component/component_diagram.fbs.bin ← symlink → file above │ filegroup case_data - │ → bundles fbs + architecture.json + expected.json + │ → bundles fbs + architecture.json + expected.yaml ▼ //.../bazel_component:bazel_component_test_data ← aggregated across all cases @@ -130,7 +129,7 @@ component/component_diagram.fbs.bin ← symlink → file above //.../bazel_component:integration_test │ │ collect_case_fbs_files() → absolute paths to .fbs.bin files - │ load_expected_fixture() → ExpectedFixture + │ load_expected_yaml_fixture() → ExpectedFixture │ run_validation_profile() → writes inputs and spawns validation_cli │ assert_cli_result() → checks pass/fail + error substrings ▼ @@ -149,7 +148,7 @@ the validator under test. ├── BUILD # architectural_design + provider_fbs_fixture_bundle + case_data ├── component_diagram.puml ├── architecture.json # Bazel build-graph snapshot (components/units) -└── expected.json +└── expected.yaml ``` ### ComponentClass cases @@ -159,7 +158,7 @@ the validator under test. ├── BUILD # architectural_design + unit_design + provider_fbs_fixture_bundle + case_data ├── component_diagram.puml ├── class_diagram.puml # one or more; multi-file: class_diagram_part1.puml, ... -└── expected.json +└── expected.yaml ``` ### ClassDesignImplementation cases @@ -180,7 +179,7 @@ the validator under test. ├── BUILD # architectural_design + provider_fbs_fixture_bundle + case_data ├── component_diagram.puml ├── sequence_diagram.puml -└── expected.json +└── expected.yaml ``` ### ComponentInternalApi cases @@ -190,7 +189,7 @@ the validator under test. ├── BUILD # architectural_design + provider_fbs_fixture_bundle + case_data ├── component_diagram.puml ├── internal_api_diagram.puml -└── expected.json +└── expected.yaml ``` ### SequenceInternalApi cases @@ -201,16 +200,13 @@ the validator under test. ├── component_diagram.puml ├── sequence_diagram.puml ├── internal_api_diagram.puml -└── expected.json +└── expected.yaml ``` -### `expected.json` / `expected.yaml` format +### `expected.yaml` format -```json -{ - "should_pass": true, - "error_contains": [] -} +```yaml +should_pass: true ``` | Field | Type | Description | @@ -220,9 +216,7 @@ the validator under test. The framework uses **substring matching** for `error_contains`, so entries do not need to reproduce exact formatting — just enough context to uniquely -identify the error. Existing suites use `expected.json`; the -`class_design_implementation` suite uses `expected.yaml` so larger multi-line -error fragments stay readable. +identify the error. ## Running the tests @@ -236,7 +230,6 @@ Run a single suite: ```bash bazel test //validation/core/integration_test/bazel_component:integration_test -bazel test //validation/core/integration_test/component_class:integration_test bazel test //validation/core/integration_test/component_sequence:integration_test bazel test //validation/core/integration_test/component_internal_api:component_internal_api_integration_test bazel test //validation/core/integration_test/sequence_internal_api:sequence_internal_api_integration_test @@ -253,7 +246,7 @@ bazel test //validation/core/integration_test/class_design_implementation:integr 2. Add the `.puml` source file(s) and — for `bazel_component` — an `architecture.json`. -3. Write `expected.json` or `expected.yaml`. For negative cases add the error +3. Write `expected.yaml`. For negative cases add the error substrings you expect to see in the CLI log. 4. Create a `BUILD` file following the pattern of an existing case in the same @@ -261,8 +254,7 @@ bazel test //validation/core/integration_test/class_design_implementation:integr 5. Add the new `case_data` target to the matching filegroup in [`BUILD`](BUILD) (`bazel_component_test_data`, - `component_class_test_data`, `component_sequence_test_data`, - `component_internal_api_test_data`, `sequence_internal_api_test_data`, + `component_sequence_test_data`, `component_internal_api_test_data`, `sequence_internal_api_test_data`, or `class_design_implementation_test_data`). 6. Add a `#[test]` function in the matching suite file, such as diff --git a/validation/core/integration_test/bazel_component/BUILD b/validation/core/integration_test/bazel_component/BUILD index 53394300..c5e71ff7 100644 --- a/validation/core/integration_test/bazel_component/BUILD +++ b/validation/core/integration_test/bazel_component/BUILD @@ -16,6 +16,10 @@ load("@rules_rust//rust:defs.bzl", "rust_test") filegroup( name = "bazel_component_test_data", srcs = [ + "//validation/core/integration_test/bazel_component/negative_duplicate_component_key:case_data", + "//validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key:case_data", + "//validation/core/integration_test/bazel_component/negative_duplicate_unit_key:case_data", + "//validation/core/integration_test/bazel_component/negative_empty_target_name_label:case_data", "//validation/core/integration_test/bazel_component/negative_extra_component:case_data", "//validation/core/integration_test/bazel_component/negative_extra_unit:case_data", "//validation/core/integration_test/bazel_component/negative_missing_component:case_data", diff --git a/validation/core/integration_test/bazel_component/bazel_component_suite.rs b/validation/core/integration_test/bazel_component/bazel_component_suite.rs index e4f4fa5b..a9bc544f 100644 --- a/validation/core/integration_test/bazel_component/bazel_component_suite.rs +++ b/validation/core/integration_test/bazel_component/bazel_component_suite.rs @@ -12,7 +12,7 @@ // ******************************************************************************* use test_framework::{ - assert_cli_result, case_file_path, collect_case_fbs_files, load_expected_fixture, + assert_cli_result, case_file_path, collect_case_fbs_files, load_expected_yaml_fixture, run_validation_profile, CliRunResult, }; @@ -34,7 +34,7 @@ fn run_case_from_cli( } fn assert_case(case_dir: &str) { - let expected = load_expected_fixture(SUITE_DIR, case_dir); + let expected = load_expected_yaml_fixture(SUITE_DIR, case_dir); let component_fbs_paths = collect_case_fbs_files(SUITE_DIR, case_dir, "component"); let result = if !component_fbs_paths.is_empty() { @@ -84,3 +84,23 @@ fn negative_extra_component_suite_case() { fn negative_wrong_stereotype_suite_case() { assert_case("negative_wrong_stereotype"); } + +#[test] +fn negative_duplicate_dependable_element_key_suite_case() { + assert_case("negative_duplicate_dependable_element_key"); +} + +#[test] +fn negative_duplicate_unit_key_suite_case() { + assert_case("negative_duplicate_unit_key"); +} + +#[test] +fn negative_duplicate_component_key_suite_case() { + assert_case("negative_duplicate_component_key"); +} + +#[test] +fn negative_empty_target_name_label_suite_case() { + assert_case("negative_empty_target_name_label"); +} diff --git a/validation/core/integration_test/component_class/positive_exact_match/BUILD b/validation/core/integration_test/bazel_component/negative_duplicate_component_key/BUILD similarity index 78% rename from validation/core/integration_test/component_class/positive_exact_match/BUILD rename to validation/core/integration_test/bazel_component/negative_duplicate_component_key/BUILD index a406dc01..4ee92b4a 100644 --- a/validation/core/integration_test/component_class/positive_exact_match/BUILD +++ b/validation/core/integration_test/bazel_component/negative_duplicate_component_key/BUILD @@ -11,34 +11,26 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design", "unit_design") +load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design") load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") architectural_design( - name = "component_design", + name = "design", static = ["component_diagram.puml"], visibility = ["//visibility:private"], ) -unit_design( - name = "class_design", - static = ["class_diagram.puml"], - visibility = ["//visibility:private"], -) - provider_fbs_fixture_bundle( name = "fbs", visibility = ["//visibility:public"], - deps = [ - ":class_design", - ":component_design", - ], + deps = [":design"], ) filegroup( name = "case_data", srcs = [ - "expected.json", + "architecture.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/negative_duplicate_component_key/architecture.json b/validation/core/integration_test/bazel_component/negative_duplicate_component_key/architecture.json new file mode 100644 index 00000000..5fef0ab0 --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_duplicate_component_key/architecture.json @@ -0,0 +1,19 @@ +{ + "components": { + "safety_software_seooc_example": { + "units": [], + "components": [ + "@//pkg_a:component_example", + "@//pkg_b:component_example" + ] + }, + "@//pkg_a:component_example": { + "units": [], + "components": [] + }, + "@//pkg_b:component_example": { + "units": [], + "components": [] + } + } +} diff --git a/validation/core/integration_test/component_class/negative_case_sensitive_mismatch/component_diagram.puml b/validation/core/integration_test/bazel_component/negative_duplicate_component_key/component_diagram.puml similarity index 80% rename from validation/core/integration_test/component_class/negative_case_sensitive_mismatch/component_diagram.puml rename to validation/core/integration_test/bazel_component/negative_duplicate_component_key/component_diagram.puml index 2a9aad3a..3b639773 100644 --- a/validation/core/integration_test/component_class/negative_case_sensitive_mismatch/component_diagram.puml +++ b/validation/core/integration_test/bazel_component/negative_duplicate_component_key/component_diagram.puml @@ -13,10 +13,8 @@ @startuml component_diagram -package "Package A" as package_a { - component "Component A" as component_a <> { - component "Unit 1" as Unit_1 <> - } +package "Sample Seooc" as safety_software_seooc_example <> { + component "Component Example" as component_example <> } @enduml diff --git a/validation/core/integration_test/bazel_component/negative_duplicate_component_key/expected.yaml b/validation/core/integration_test/bazel_component/negative_duplicate_component_key/expected.yaml new file mode 100644 index 00000000..44ced1ac --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_duplicate_component_key/expected.yaml @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Design] Component "component_example" is defined more than once in Bazel. + Alias : "component_example" + Parent : safety_software_seooc_example + First bazel label : @//pkg_a:component_example + Second bazel label : @//pkg_b:component_example + Fix : Keep only one Bazel component definition for "component_example" under "safety_software_seooc_example", or rename one of the duplicate Bazel targets. diff --git a/validation/core/integration_test/component_class/negative_boundary_mismatch/BUILD b/validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/BUILD similarity index 78% rename from validation/core/integration_test/component_class/negative_boundary_mismatch/BUILD rename to validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/BUILD index a406dc01..4ee92b4a 100644 --- a/validation/core/integration_test/component_class/negative_boundary_mismatch/BUILD +++ b/validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/BUILD @@ -11,34 +11,26 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design", "unit_design") +load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design") load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") architectural_design( - name = "component_design", + name = "design", static = ["component_diagram.puml"], visibility = ["//visibility:private"], ) -unit_design( - name = "class_design", - static = ["class_diagram.puml"], - visibility = ["//visibility:private"], -) - provider_fbs_fixture_bundle( name = "fbs", visibility = ["//visibility:public"], - deps = [ - ":class_design", - ":component_design", - ], + deps = [":design"], ) filegroup( name = "case_data", srcs = [ - "expected.json", + "architecture.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/architecture.json b/validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/architecture.json new file mode 100644 index 00000000..b9caac5b --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/architecture.json @@ -0,0 +1,20 @@ +{ + "components": { + "@//pkg1:safety_software_seooc_example": { + "units": [], + "components": [ + "@//pkg1:component_example" + ] + }, + "@//pkg2:safety_software_seooc_example": { + "units": [], + "components": [] + }, + "@//pkg1:component_example": { + "units": [ + "@//pkg1/unit_1:unit_1" + ], + "components": [] + } + } +} diff --git a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/component_diagram_part1.puml b/validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/component_diagram.puml similarity index 82% rename from validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/component_diagram_part1.puml rename to validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/component_diagram.puml index 5fb313f0..e75068c5 100644 --- a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/component_diagram_part1.puml +++ b/validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/component_diagram.puml @@ -13,8 +13,8 @@ @startuml component_diagram -package "Package A" as package_a { - component "Component A" as component_a <> { +package "Sample Seooc" as safety_software_seooc_example <> { + component "Component Example" as component_example <> { component "Unit 1" as unit_1 <> } } diff --git a/validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/expected.yaml b/validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/expected.yaml new file mode 100644 index 00000000..39b2556c --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_duplicate_dependable_element_key/expected.yaml @@ -0,0 +1,19 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Design] Dependable element "safety_software_seooc_example" is defined more than once in Bazel. + Alias : "safety_software_seooc_example" + First bazel label : @//pkg1:safety_software_seooc_example + Second bazel label : @//pkg2:safety_software_seooc_example + Fix : Keep only one Bazel dependable element definition for "safety_software_seooc_example", or rename one of the duplicate Bazel targets. diff --git a/validation/core/integration_test/component_class/negative_case_sensitive_mismatch/BUILD b/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/BUILD similarity index 78% rename from validation/core/integration_test/component_class/negative_case_sensitive_mismatch/BUILD rename to validation/core/integration_test/bazel_component/negative_duplicate_unit_key/BUILD index a406dc01..4ee92b4a 100644 --- a/validation/core/integration_test/component_class/negative_case_sensitive_mismatch/BUILD +++ b/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/BUILD @@ -11,34 +11,26 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design", "unit_design") +load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design") load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") architectural_design( - name = "component_design", + name = "design", static = ["component_diagram.puml"], visibility = ["//visibility:private"], ) -unit_design( - name = "class_design", - static = ["class_diagram.puml"], - visibility = ["//visibility:private"], -) - provider_fbs_fixture_bundle( name = "fbs", visibility = ["//visibility:public"], - deps = [ - ":class_design", - ":component_design", - ], + deps = [":design"], ) filegroup( name = "case_data", srcs = [ - "expected.json", + "architecture.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/architecture.json b/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/architecture.json new file mode 100644 index 00000000..5220d232 --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/architecture.json @@ -0,0 +1,17 @@ +{ + "components": { + "safety_software_seooc_example": { + "units": [], + "components": [ + "@//pkg:component_example" + ] + }, + "@//pkg:component_example": { + "units": [ + "@//pkg_a/unit_1:unit_1", + "@//pkg_b/unit_1:unit_1" + ], + "components": [] + } + } +} diff --git a/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/component_diagram.puml b/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/component_diagram.puml new file mode 100644 index 00000000..e75068c5 --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/component_diagram.puml @@ -0,0 +1,22 @@ +' ******************************************************************************* +' 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 +' ******************************************************************************* + +@startuml component_diagram + +package "Sample Seooc" as safety_software_seooc_example <> { + component "Component Example" as component_example <> { + component "Unit 1" as unit_1 <> + } +} + +@enduml diff --git a/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/expected.yaml b/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/expected.yaml new file mode 100644 index 00000000..d5e2655d --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_duplicate_unit_key/expected.yaml @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Design] Unit "unit_1" is defined more than once in Bazel. + Alias : "unit_1" + Parent : component_example + First bazel label : @//pkg_a/unit_1:unit_1 + Second bazel label : @//pkg_b/unit_1:unit_1 + Fix : Keep only one Bazel unit definition for "unit_1" under "component_example", or rename one of the duplicate Bazel targets. diff --git a/validation/core/integration_test/component_class/negative_missing_namespace_coverage/BUILD b/validation/core/integration_test/bazel_component/negative_empty_target_name_label/BUILD similarity index 78% rename from validation/core/integration_test/component_class/negative_missing_namespace_coverage/BUILD rename to validation/core/integration_test/bazel_component/negative_empty_target_name_label/BUILD index a406dc01..4ee92b4a 100644 --- a/validation/core/integration_test/component_class/negative_missing_namespace_coverage/BUILD +++ b/validation/core/integration_test/bazel_component/negative_empty_target_name_label/BUILD @@ -11,34 +11,26 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design", "unit_design") +load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design") load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") architectural_design( - name = "component_design", + name = "design", static = ["component_diagram.puml"], visibility = ["//visibility:private"], ) -unit_design( - name = "class_design", - static = ["class_diagram.puml"], - visibility = ["//visibility:private"], -) - provider_fbs_fixture_bundle( name = "fbs", visibility = ["//visibility:public"], - deps = [ - ":class_design", - ":component_design", - ], + deps = [":design"], ) filegroup( name = "case_data", srcs = [ - "expected.json", + "architecture.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/negative_empty_target_name_label/architecture.json b/validation/core/integration_test/bazel_component/negative_empty_target_name_label/architecture.json new file mode 100644 index 00000000..d649d9f5 --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_empty_target_name_label/architecture.json @@ -0,0 +1,10 @@ +{ + "components": { + "safety_software_seooc_example": { + "units": [], + "components": [ + "@//pkg:" + ] + } + } +} diff --git a/validation/core/integration_test/bazel_component/negative_empty_target_name_label/component_diagram.puml b/validation/core/integration_test/bazel_component/negative_empty_target_name_label/component_diagram.puml new file mode 100644 index 00000000..3b639773 --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_empty_target_name_label/component_diagram.puml @@ -0,0 +1,20 @@ +' ******************************************************************************* +' 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 +' ******************************************************************************* + +@startuml component_diagram + +package "Sample Seooc" as safety_software_seooc_example <> { + component "Component Example" as component_example <> +} + +@enduml diff --git a/validation/core/integration_test/bazel_component/negative_empty_target_name_label/expected.yaml b/validation/core/integration_test/bazel_component/negative_empty_target_name_label/expected.yaml new file mode 100644 index 00000000..aedb8058 --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_empty_target_name_label/expected.yaml @@ -0,0 +1,17 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Design] Bazel label "@//pkg:" does not define a target. + Bazel label : "@//pkg:" + Fix : Add a target name after ':' in Bazel label "@//pkg:". diff --git a/validation/core/integration_test/bazel_component/negative_extra_component/BUILD b/validation/core/integration_test/bazel_component/negative_extra_component/BUILD index 8fe623cf..4ee92b4a 100644 --- a/validation/core/integration_test/bazel_component/negative_extra_component/BUILD +++ b/validation/core/integration_test/bazel_component/negative_extra_component/BUILD @@ -30,7 +30,7 @@ filegroup( name = "case_data", srcs = [ "architecture.json", - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/negative_extra_component/expected.json b/validation/core/integration_test/bazel_component/negative_extra_component/expected.json deleted file mode 100644 index 5cd28dc7..00000000 --- a/validation/core/integration_test/bazel_component/negative_extra_component/expected.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "Extra component in PlantUML not in Bazel", - "Alias : \"extra_component\"" - ] -} diff --git a/validation/core/integration_test/bazel_component/negative_extra_component/expected.yaml b/validation/core/integration_test/bazel_component/negative_extra_component/expected.yaml new file mode 100644 index 00000000..d07de74f --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_extra_component/expected.yaml @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Naming] Component "extra_component" from the PlantUML component diagram not found in Bazel. + Alias : "extra_component" + Parent : safety_software_seooc_example + Component source file : "validation/core/integration_test/bazel_component/negative_extra_component/component_diagram.puml" + Component source line : 22 + Fix : Add the corresponding Bazel component definition for "extra_component", or remove it from the PlantUML component diagram. diff --git a/validation/core/integration_test/bazel_component/negative_extra_unit/BUILD b/validation/core/integration_test/bazel_component/negative_extra_unit/BUILD index 8fe623cf..4ee92b4a 100644 --- a/validation/core/integration_test/bazel_component/negative_extra_unit/BUILD +++ b/validation/core/integration_test/bazel_component/negative_extra_unit/BUILD @@ -30,7 +30,7 @@ filegroup( name = "case_data", srcs = [ "architecture.json", - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/negative_extra_unit/expected.json b/validation/core/integration_test/bazel_component/negative_extra_unit/expected.json deleted file mode 100644 index fd226e85..00000000 --- a/validation/core/integration_test/bazel_component/negative_extra_unit/expected.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "Extra unit in PlantUML not in Bazel", - "Alias : \"unit_3\"" - ] -} diff --git a/validation/core/integration_test/bazel_component/negative_extra_unit/expected.yaml b/validation/core/integration_test/bazel_component/negative_extra_unit/expected.yaml new file mode 100644 index 00000000..2cdc25f6 --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_extra_unit/expected.yaml @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Naming] Unit "unit_3" from the PlantUML component diagram not found in Bazel. + Alias : "unit_3" + Parent : component_example + Component source file : "validation/core/integration_test/bazel_component/negative_extra_unit/component_diagram.puml" + Component source line : 20 + Fix : Add the corresponding Bazel unit definition for "unit_3", or remove it from the PlantUML component diagram. diff --git a/validation/core/integration_test/bazel_component/negative_missing_component/BUILD b/validation/core/integration_test/bazel_component/negative_missing_component/BUILD index 8fe623cf..4ee92b4a 100644 --- a/validation/core/integration_test/bazel_component/negative_missing_component/BUILD +++ b/validation/core/integration_test/bazel_component/negative_missing_component/BUILD @@ -30,7 +30,7 @@ filegroup( name = "case_data", srcs = [ "architecture.json", - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/negative_missing_component/expected.json b/validation/core/integration_test/bazel_component/negative_missing_component/expected.json deleted file mode 100644 index 51190b3f..00000000 --- a/validation/core/integration_test/bazel_component/negative_missing_component/expected.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "Missing component in PlantUML", - "Alias : \"component_example\"" - ] -} diff --git a/validation/core/integration_test/bazel_component/negative_missing_component/expected.yaml b/validation/core/integration_test/bazel_component/negative_missing_component/expected.yaml new file mode 100644 index 00000000..1d824ebb --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_missing_component/expected.yaml @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Naming] Component "component_example" from Bazel not found in the PlantUML component diagram. + Alias : "component_example" + Parent : safety_software_seooc_example + Stereotype : <> + Bazel label : @//bazel/rules/rules_score/examples/seooc:component_example + Fix : Add component "component_example" with stereotype <> in the PlantUML component diagram, or remove it from Bazel. diff --git a/validation/core/integration_test/bazel_component/negative_missing_unit/BUILD b/validation/core/integration_test/bazel_component/negative_missing_unit/BUILD index 8fe623cf..4ee92b4a 100644 --- a/validation/core/integration_test/bazel_component/negative_missing_unit/BUILD +++ b/validation/core/integration_test/bazel_component/negative_missing_unit/BUILD @@ -30,7 +30,7 @@ filegroup( name = "case_data", srcs = [ "architecture.json", - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/negative_missing_unit/expected.json b/validation/core/integration_test/bazel_component/negative_missing_unit/expected.json deleted file mode 100644 index 049f98f8..00000000 --- a/validation/core/integration_test/bazel_component/negative_missing_unit/expected.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "Missing unit in PlantUML", - "Alias : \"unit_2\"" - ] -} diff --git a/validation/core/integration_test/bazel_component/negative_missing_unit/expected.yaml b/validation/core/integration_test/bazel_component/negative_missing_unit/expected.yaml new file mode 100644 index 00000000..a46e3019 --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_missing_unit/expected.yaml @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Naming] Unit "unit_2" from Bazel not found in the PlantUML component diagram. + Alias : "unit_2" + Parent : component_example + Stereotype : <> + Bazel label : @//bazel/rules/rules_score/examples/seooc/unit_2:unit_2 + Fix : Add unit "unit_2" with stereotype <> in the PlantUML component diagram, or remove it from Bazel. diff --git a/validation/core/integration_test/bazel_component/negative_wrong_stereotype/BUILD b/validation/core/integration_test/bazel_component/negative_wrong_stereotype/BUILD index 8fe623cf..4ee92b4a 100644 --- a/validation/core/integration_test/bazel_component/negative_wrong_stereotype/BUILD +++ b/validation/core/integration_test/bazel_component/negative_wrong_stereotype/BUILD @@ -30,7 +30,7 @@ filegroup( name = "case_data", srcs = [ "architecture.json", - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/negative_wrong_stereotype/expected.json b/validation/core/integration_test/bazel_component/negative_wrong_stereotype/expected.json deleted file mode 100644 index 51190b3f..00000000 --- a/validation/core/integration_test/bazel_component/negative_wrong_stereotype/expected.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "Missing component in PlantUML", - "Alias : \"component_example\"" - ] -} diff --git a/validation/core/integration_test/bazel_component/negative_wrong_stereotype/expected.yaml b/validation/core/integration_test/bazel_component/negative_wrong_stereotype/expected.yaml new file mode 100644 index 00000000..1d824ebb --- /dev/null +++ b/validation/core/integration_test/bazel_component/negative_wrong_stereotype/expected.yaml @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Naming] Component "component_example" from Bazel not found in the PlantUML component diagram. + Alias : "component_example" + Parent : safety_software_seooc_example + Stereotype : <> + Bazel label : @//bazel/rules/rules_score/examples/seooc:component_example + Fix : Add component "component_example" with stereotype <> in the PlantUML component diagram, or remove it from Bazel. diff --git a/validation/core/integration_test/bazel_component/positive_case_insensitive/BUILD b/validation/core/integration_test/bazel_component/positive_case_insensitive/BUILD index 8fe623cf..4ee92b4a 100644 --- a/validation/core/integration_test/bazel_component/positive_case_insensitive/BUILD +++ b/validation/core/integration_test/bazel_component/positive_case_insensitive/BUILD @@ -30,7 +30,7 @@ filegroup( name = "case_data", srcs = [ "architecture.json", - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/positive_case_insensitive/expected.json b/validation/core/integration_test/bazel_component/positive_case_insensitive/expected.json deleted file mode 100644 index 208a55e1..00000000 --- a/validation/core/integration_test/bazel_component/positive_case_insensitive/expected.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "should_pass": true, - "error_contains": [] -} diff --git a/validation/core/integration_test/bazel_component/positive_case_insensitive/expected.yaml b/validation/core/integration_test/bazel_component/positive_case_insensitive/expected.yaml new file mode 100644 index 00000000..898ecba3 --- /dev/null +++ b/validation/core/integration_test/bazel_component/positive_case_insensitive/expected.yaml @@ -0,0 +1,13 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: true diff --git a/validation/core/integration_test/bazel_component/positive_component/BUILD b/validation/core/integration_test/bazel_component/positive_component/BUILD index 8fe623cf..4ee92b4a 100644 --- a/validation/core/integration_test/bazel_component/positive_component/BUILD +++ b/validation/core/integration_test/bazel_component/positive_component/BUILD @@ -30,7 +30,7 @@ filegroup( name = "case_data", srcs = [ "architecture.json", - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/bazel_component/positive_component/expected.json b/validation/core/integration_test/bazel_component/positive_component/expected.json deleted file mode 100644 index 208a55e1..00000000 --- a/validation/core/integration_test/bazel_component/positive_component/expected.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "should_pass": true, - "error_contains": [] -} diff --git a/validation/core/integration_test/bazel_component/positive_component/expected.yaml b/validation/core/integration_test/bazel_component/positive_component/expected.yaml new file mode 100644 index 00000000..898ecba3 --- /dev/null +++ b/validation/core/integration_test/bazel_component/positive_component/expected.yaml @@ -0,0 +1,13 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: true diff --git a/validation/core/integration_test/class_design_implementation/BUILD b/validation/core/integration_test/class_design_implementation/BUILD index 5c7825ae..c6052d0b 100644 --- a/validation/core/integration_test/class_design_implementation/BUILD +++ b/validation/core/integration_test/class_design_implementation/BUILD @@ -17,6 +17,7 @@ filegroup( name = "class_design_implementation_test_data", srcs = [ "//validation/core/integration_test/class_design_implementation/negative_class_member_missing:case_data", + "//validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input:case_data", "//validation/core/integration_test/class_design_implementation/negative_class_missing:case_data", "//validation/core/integration_test/class_design_implementation/negative_entity_type_mismatch:case_data", "//validation/core/integration_test/class_design_implementation/negative_enum_literal_mismatch:case_data", diff --git a/validation/core/integration_test/class_design_implementation/class_design_implementation_suite.rs b/validation/core/integration_test/class_design_implementation/class_design_implementation_suite.rs index 2dbab8da..f82bf716 100644 --- a/validation/core/integration_test/class_design_implementation/class_design_implementation_suite.rs +++ b/validation/core/integration_test/class_design_implementation/class_design_implementation_suite.rs @@ -92,6 +92,11 @@ fn negative_class_member_missing() { assert_case("negative_class_member_missing"); } +#[test] +fn negative_duplicate_class_entity_input() { + assert_case("negative_duplicate_class_entity_input"); +} + #[test] fn negative_entity_type_mismatch() { assert_case("negative_entity_type_mismatch"); diff --git a/validation/core/integration_test/class_design_implementation/negative_class_member_missing/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_class_member_missing/expected.yaml index cb112d2c..6825c7bc 100644 --- a/validation/core/integration_test/class_design_implementation/negative_class_member_missing/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_class_member_missing/expected.yaml @@ -12,9 +12,9 @@ # ******************************************************************************* should_pass: false error_contains: | - Missing implementation variable for unit design entity: - Entity ID : Engine - Member : manufacturer - Design source file : class_diagram.puml - Design source line : 18 - Required Action : Implement the member or update the unit design + [Member] Variable "manufacturer" from entity "Engine" in the unit design not found in the C++ class implementation. + Entity : "Engine" + Member : variable "manufacturer" + Design source file : "validation/core/integration_test/class_design_implementation/negative_class_member_missing/class_diagram.puml" + Design source line : 18 + Fix : Add variable "manufacturer" to entity "Engine" in the C++ class implementation, or remove it from the unit design. diff --git a/validation/core/integration_test/class_design_implementation/negative_class_missing/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_class_missing/expected.yaml index 4565a9db..f8d2f021 100644 --- a/validation/core/integration_test/class_design_implementation/negative_class_missing/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_class_missing/expected.yaml @@ -12,8 +12,8 @@ # ******************************************************************************* should_pass: false error_contains: | - Missing implementation class for unit design entity: - Entity ID : Manufacturer - Design source file : class_diagram.puml - Design source line : 18 - Required Action : Add a matching implementation class or update the unit design + [Class] Class "Manufacturer" from the unit design not found in the C++ class implementation. + Class : "Manufacturer" + Design source file : "validation/core/integration_test/class_design_implementation/negative_class_missing/class_diagram.puml" + Design source line : 18 + Fix : Add implementation class "Manufacturer" in the C++ class implementation, or remove it from the unit design. diff --git a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/BUILD b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/BUILD similarity index 65% rename from validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/BUILD rename to validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/BUILD index 49093d4a..37397c86 100644 --- a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/BUILD +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/BUILD @@ -11,20 +11,11 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design", "unit_design") +load("//bazel/rules/rules_score:rules_score.bzl", "unit", "unit_design") load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") -architectural_design( - name = "component_design", - static = [ - "component_diagram_part1.puml", - "component_diagram_part2.puml", - ], - visibility = ["//visibility:private"], -) - unit_design( - name = "class_design", + name = "unit_design", static = [ "class_diagram_part1.puml", "class_diagram_part2.puml", @@ -32,20 +23,35 @@ unit_design( visibility = ["//visibility:private"], ) +cc_library( + name = "transport", + srcs = ["transport.cpp"], +) + +unit( + name = "unit", + testonly = False, + maturity = "development", + tests = [], + unit_design = [":unit_design"], + visibility = ["//visibility:private"], + implementation = [":transport"], +) + provider_fbs_fixture_bundle( name = "fbs", - visibility = ["//visibility:public"], + visibility = ["//visibility:private"], deps = [ - ":class_design", - ":component_design", + ":unit", + ":unit_design", ], ) filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], - visibility = ["//visibility:public"], + visibility = ["//validation/core/integration_test:__subpackages__"], ) diff --git a/validation/core/integration_test/component_class/positive_multi_class_files/class_diagram_part1.puml b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/class_diagram_part1.puml similarity index 85% rename from validation/core/integration_test/component_class/positive_multi_class_files/class_diagram_part1.puml rename to validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/class_diagram_part1.puml index e8ab88f8..f5c0fd55 100644 --- a/validation/core/integration_test/component_class/positive_multi_class_files/class_diagram_part1.puml +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/class_diagram_part1.puml @@ -11,12 +11,12 @@ ' SPDX-License-Identifier: Apache-2.0 ' ******************************************************************************* -@startuml class_diagram +@startuml class_diagram_part1 -namespace unit_1 { - class Foo { - + GetNumber() : uint8_t - } +skinparam classAttributeIconSize 0 + +class Engine { + + start() : void } @enduml diff --git a/validation/core/integration_test/component_class/positive_multi_class_files/class_diagram_part2.puml b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/class_diagram_part2.puml similarity index 85% rename from validation/core/integration_test/component_class/positive_multi_class_files/class_diagram_part2.puml rename to validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/class_diagram_part2.puml index 0b5b760e..dfaa1427 100644 --- a/validation/core/integration_test/component_class/positive_multi_class_files/class_diagram_part2.puml +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/class_diagram_part2.puml @@ -11,12 +11,12 @@ ' SPDX-License-Identifier: Apache-2.0 ' ******************************************************************************* -@startuml class_diagram +@startuml class_diagram_part2 -namespace unit_2 { - class Bar { - + AssertNumber() : bool - } +skinparam classAttributeIconSize 0 + +class engine { + + stop() : void } @enduml diff --git a/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/expected.yaml new file mode 100644 index 00000000..de0b2fda --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/expected.yaml @@ -0,0 +1,21 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Class] Class "Engine" is defined more than once in the class diagram. + Class : "Engine" + Design source file : "validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/class_diagram_part1.puml" + Design source line : 18 + Duplicate source file : "validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/class_diagram_part2.puml" + Duplicate source line : 18 + Fix : Remove or rename one of the duplicate class "Engine" declarations in the class diagram. diff --git a/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/transport.cpp b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/transport.cpp new file mode 100644 index 00000000..78a0562e --- /dev/null +++ b/validation/core/integration_test/class_design_implementation/negative_duplicate_class_entity_input/transport.cpp @@ -0,0 +1,18 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +class Engine +{ + public: + void start(); +}; diff --git a/validation/core/integration_test/class_design_implementation/negative_entity_type_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_entity_type_mismatch/expected.yaml index 7177ec67..50adc926 100644 --- a/validation/core/integration_test/class_design_implementation/negative_entity_type_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_entity_type_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : entity_type - Design value : Class - Design source file : class_diagram.puml - Design source line : 16 - Implement value : Struct - Implement source file : validation/core/integration_test/class_design_implementation/negative_entity_type_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field "entity_type" on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : entity_type + Design value : Class + Design source file : "validation/core/integration_test/class_design_implementation/negative_entity_type_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : Struct + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_entity_type_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make "entity_type" in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_enum_literal_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_enum_literal_mismatch/expected.yaml index 5add080e..6dd2e26c 100644 --- a/validation/core/integration_test/class_design_implementation/negative_enum_literal_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_enum_literal_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Mode - Field : "On" - Design value : 2 - Design source file : class_diagram.puml - Design source line : 16 - Implement value : 1 - Implement source file : validation/core/integration_test/class_design_implementation/negative_enum_literal_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field "On" on entity "Mode" differs between the unit design and the C++ class implementation. + Entity : "Mode" + Field : "On" + Design value : 2 + Design source file : "validation/core/integration_test/class_design_implementation/negative_enum_literal_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : 1 + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_enum_literal_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make "On" in entity "Mode" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_enum_literal_missing/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_enum_literal_missing/expected.yaml index 88f6623d..7d13e923 100644 --- a/validation/core/integration_test/class_design_implementation/negative_enum_literal_missing/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_enum_literal_missing/expected.yaml @@ -12,9 +12,9 @@ # ******************************************************************************* should_pass: false error_contains: | - Missing implementation enum_literal for unit design entity: - Entity ID : Mode - Member : On - Design source file : class_diagram.puml - Design source line : 16 - Required Action : Implement the member or update the unit design + [Member] Enum_literal "On" from entity "Mode" in the unit design not found in the C++ class implementation. + Entity : "Mode" + Member : enum_literal "On" + Design source file : "validation/core/integration_test/class_design_implementation/negative_enum_literal_missing/class_diagram.puml" + Design source line : 16 + Fix : Add enum_literal "On" to entity "Mode" in the C++ class implementation, or remove it from the unit design. diff --git a/validation/core/integration_test/class_design_implementation/negative_method_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_method_mismatch/expected.yaml index 0ddb1de6..fdee77f1 100644 --- a/validation/core/integration_test/class_design_implementation/negative_method_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_method_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : method "stop" return_type - Design value : int - Design source file : class_diagram.puml - Design source line : 16 - Implement value : void - Implement source file : validation/core/integration_test/class_design_implementation/negative_method_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field method "stop" return type on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : method "stop" return type + Design value : int + Design source file : "validation/core/integration_test/class_design_implementation/negative_method_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : void + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_method_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make method "stop" return type in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_method_missing/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_method_missing/expected.yaml index 01519c5b..d3657880 100644 --- a/validation/core/integration_test/class_design_implementation/negative_method_missing/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_method_missing/expected.yaml @@ -12,9 +12,9 @@ # ******************************************************************************* should_pass: false error_contains: | - Missing implementation method for unit design entity: - Entity ID : Engine - Member : restart() - Design source file : class_diagram.puml - Design source line : 16 - Required Action : Implement the member or update the unit design + [Member] Method "restart()" from entity "Engine" in the unit design not found in the C++ class implementation. + Entity : "Engine" + Member : method "restart()" + Design source file : "validation/core/integration_test/class_design_implementation/negative_method_missing/class_diagram.puml" + Design source line : 16 + Fix : Add method "restart()" to entity "Engine" in the C++ class implementation, or remove it from the unit design. diff --git a/validation/core/integration_test/class_design_implementation/negative_method_modifier_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_method_modifier_mismatch/expected.yaml index fe70a293..a0ed4b5a 100644 --- a/validation/core/integration_test/class_design_implementation/negative_method_modifier_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_method_modifier_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : method "stop" modifiers - Design value : [] - Design source file : class_diagram.puml - Design source line : 16 - Implement value : [Static] - Implement source file : validation/core/integration_test/class_design_implementation/negative_method_modifier_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field method "stop" modifiers on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : method "stop" modifiers + Design value : [] + Design source file : "validation/core/integration_test/class_design_implementation/negative_method_modifier_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : [Static] + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_method_modifier_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make method "stop" modifiers in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_method_parameter_count_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_method_parameter_count_mismatch/expected.yaml index fd993fc0..28067d33 100644 --- a/validation/core/integration_test/class_design_implementation/negative_method_parameter_count_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_method_parameter_count_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : method "stop" parameter_count - Design value : 2 parameters (mode: int, force: int) - Design source file : class_diagram.puml - Design source line : 16 - Implement value : 1 parameter (mode: int) - Implement source file : validation/core/integration_test/class_design_implementation/negative_method_parameter_count_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field method "stop" parameter count on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : method "stop" parameter count + Design value : 2 parameters (mode: int, force: int) + Design source file : "validation/core/integration_test/class_design_implementation/negative_method_parameter_count_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : 1 parameter (mode: int) + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_method_parameter_count_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make method "stop" parameter count in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_method_parameter_name_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_method_parameter_name_mismatch/expected.yaml index 6699635b..5c12c9f4 100644 --- a/validation/core/integration_test/class_design_implementation/negative_method_parameter_name_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_method_parameter_name_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : method "stop" parameter name - Design value : force: double - Design source file : class_diagram.puml - Design source line : 16 - Implement value : thrust: double - Implement source file : validation/core/integration_test/class_design_implementation/negative_method_parameter_name_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field method "stop" parameter name on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : method "stop" parameter name + Design value : force: double + Design source file : "validation/core/integration_test/class_design_implementation/negative_method_parameter_name_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : thrust: double + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_method_parameter_name_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make method "stop" parameter name in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_method_parameter_type_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_method_parameter_type_mismatch/expected.yaml index e845c834..2cac6df8 100644 --- a/validation/core/integration_test/class_design_implementation/negative_method_parameter_type_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_method_parameter_type_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : method "stop" parameter type - Design value : force: int - Design source file : class_diagram.puml - Design source line : 16 - Implement value : force: double - Implement source file : validation/core/integration_test/class_design_implementation/negative_method_parameter_type_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field method "stop" parameter type on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : method "stop" parameter type + Design value : force: int + Design source file : "validation/core/integration_test/class_design_implementation/negative_method_parameter_type_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : force: double + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_method_parameter_type_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make method "stop" parameter type in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_method_visibility_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_method_visibility_mismatch/expected.yaml index 81f6a107..b90a421e 100644 --- a/validation/core/integration_test/class_design_implementation/negative_method_visibility_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_method_visibility_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : method "stop" visibility - Design value : Public - Design source file : class_diagram.puml - Design source line : 16 - Implement value : Private - Implement source file : validation/core/integration_test/class_design_implementation/negative_method_visibility_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field method "stop" visibility on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : method "stop" visibility + Design value : Public + Design source file : "validation/core/integration_test/class_design_implementation/negative_method_visibility_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : Private + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_method_visibility_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make method "stop" visibility in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_relationship_missing/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_relationship_missing/expected.yaml index f51da9ae..6445c8c2 100644 --- a/validation/core/integration_test/class_design_implementation/negative_relationship_missing/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_relationship_missing/expected.yaml @@ -12,9 +12,9 @@ # ******************************************************************************* should_pass: false error_contains: | - Missing implementation relationship for unit design entity: - Entity ID : Manufacturer - Member : Manufacturer -> Composition -> Engine - Design source file : class_diagram.puml - Design source line : 17 - Required Action : Implement the member or update the unit design + [Member] Relationship "Manufacturer -> Composition -> Engine" from entity "Manufacturer" in the unit design not found in the C++ class implementation. + Entity : "Manufacturer" + Member : relationship "Manufacturer -> Composition -> Engine" + Design source file : "validation/core/integration_test/class_design_implementation/negative_relationship_missing/class_diagram.puml" + Design source line : 17 + Fix : Add relationship "Manufacturer -> Composition -> Engine" to entity "Manufacturer" in the C++ class implementation, or remove it from the unit design. diff --git a/validation/core/integration_test/class_design_implementation/negative_relationship_type_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_relationship_type_mismatch/expected.yaml index ee56a4f1..3bbc7b5b 100644 --- a/validation/core/integration_test/class_design_implementation/negative_relationship_type_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_relationship_type_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : relationship type - Design value : Engine -> Aggregation -> Manufacturer - Design source file : class_diagram.puml - Design source line : 18 - Implement value : Engine -> Composition -> Manufacturer - Implement source file : validation/core/integration_test/class_design_implementation/negative_relationship_type_mismatch/transport.cpp - Implement source line : 18 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field "relationship type" on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : relationship type + Design value : Engine -> Aggregation -> Manufacturer + Design source file : "validation/core/integration_test/class_design_implementation/negative_relationship_type_mismatch/class_diagram.puml" + Design source line : 18 + Implementation value : Engine -> Composition -> Manufacturer + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_relationship_type_mismatch/transport.cpp" + Implementation source line : 18 + Fix : Make "relationship type" in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_template_parameter_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_template_parameter_mismatch/expected.yaml index 79ba985b..4493a191 100644 --- a/validation/core/integration_test/class_design_implementation/negative_template_parameter_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_template_parameter_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Box - Field : template_parameters - Design value : ["T"] - Design source file : class_diagram.puml - Design source line : 16 - Implement value : ["U"] - Implement source file : validation/core/integration_test/class_design_implementation/negative_template_parameter_mismatch/transport.cpp - Implement source line : 15 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field "template_parameters" on entity "Box" differs between the unit design and the C++ class implementation. + Entity : "Box" + Field : template_parameters + Design value : ["T"] + Design source file : "validation/core/integration_test/class_design_implementation/negative_template_parameter_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : ["U"] + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_template_parameter_mismatch/transport.cpp" + Implementation source line : 15 + Fix : Make "template_parameters" in entity "Box" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_template_parameter_pack_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_template_parameter_pack_mismatch/expected.yaml index af4168a5..df8c5614 100644 --- a/validation/core/integration_test/class_design_implementation/negative_template_parameter_pack_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_template_parameter_pack_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Box - Field : template_parameters - Design value : ["T..."] - Design source file : class_diagram.puml - Design source line : 16 - Implement value : ["T"] - Implement source file : validation/core/integration_test/class_design_implementation/negative_template_parameter_pack_mismatch/transport.cpp - Implement source line : 15 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field "template_parameters" on entity "Box" differs between the unit design and the C++ class implementation. + Entity : "Box" + Field : template_parameters + Design value : ["T..."] + Design source file : "validation/core/integration_test/class_design_implementation/negative_template_parameter_pack_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : ["T"] + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_template_parameter_pack_mismatch/transport.cpp" + Implementation source line : 15 + Fix : Make "template_parameters" in entity "Box" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_type_alias_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_type_alias_mismatch/expected.yaml index a75848c1..385ece5a 100644 --- a/validation/core/integration_test/class_design_implementation/negative_type_alias_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_type_alias_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : type_alias "Speed" original_type - Design value : std::uint8_t - Design source file : class_diagram.puml - Design source line : 16 - Implement value : std::uint16_t - Implement source file : validation/core/integration_test/class_design_implementation/negative_type_alias_mismatch/transport.cpp - Implement source line : 16 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field type alias "Speed" original type on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : type alias "Speed" original type + Design value : std::uint8_t + Design source file : "validation/core/integration_test/class_design_implementation/negative_type_alias_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : std::uint16_t + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_type_alias_mismatch/transport.cpp" + Implementation source line : 16 + Fix : Make type alias "Speed" original type in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_type_alias_missing/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_type_alias_missing/expected.yaml index 4b3a28ee..1b9a5675 100644 --- a/validation/core/integration_test/class_design_implementation/negative_type_alias_missing/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_type_alias_missing/expected.yaml @@ -12,9 +12,9 @@ # ******************************************************************************* should_pass: false error_contains: | - Missing implementation type_alias for unit design entity: - Entity ID : Engine - Member : Speed - Design source file : class_diagram.puml - Design source line : 16 - Required Action : Implement the member or update the unit design + [Member] Type_alias "Speed" from entity "Engine" in the unit design not found in the C++ class implementation. + Entity : "Engine" + Member : type_alias "Speed" + Design source file : "validation/core/integration_test/class_design_implementation/negative_type_alias_missing/class_diagram.puml" + Design source line : 16 + Fix : Add type_alias "Speed" to entity "Engine" in the C++ class implementation, or remove it from the unit design. diff --git a/validation/core/integration_test/class_design_implementation/negative_variable_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_variable_mismatch/expected.yaml index 4aa3e4ec..bb212713 100644 --- a/validation/core/integration_test/class_design_implementation/negative_variable_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_variable_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : variable "speed" data_type - Design value : int - Design source file : class_diagram.puml - Design source line : 16 - Implement value : double - Implement source file : validation/core/integration_test/class_design_implementation/negative_variable_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field variable "speed" data type on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : variable "speed" data type + Design value : int + Design source file : "validation/core/integration_test/class_design_implementation/negative_variable_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : double + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_variable_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make variable "speed" data type in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_variable_static_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_variable_static_mismatch/expected.yaml index 566f7dbb..2f9ab95b 100644 --- a/validation/core/integration_test/class_design_implementation/negative_variable_static_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_variable_static_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : variable "speed" is_static - Design value : false - Design source file : class_diagram.puml - Design source line : 16 - Implement value : true - Implement source file : validation/core/integration_test/class_design_implementation/negative_variable_static_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field variable "speed" is static on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : variable "speed" is static + Design value : false + Design source file : "validation/core/integration_test/class_design_implementation/negative_variable_static_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : true + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_variable_static_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make variable "speed" is static in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/class_design_implementation/negative_variable_visibility_mismatch/expected.yaml b/validation/core/integration_test/class_design_implementation/negative_variable_visibility_mismatch/expected.yaml index 907ee96b..5dd3fd8a 100644 --- a/validation/core/integration_test/class_design_implementation/negative_variable_visibility_mismatch/expected.yaml +++ b/validation/core/integration_test/class_design_implementation/negative_variable_visibility_mismatch/expected.yaml @@ -12,13 +12,13 @@ # ******************************************************************************* should_pass: false error_contains: | - Implementation class data differs from unit design entity: - Entity ID : Engine - Field : variable "speed" visibility - Design value : Public - Design source file : class_diagram.puml - Design source line : 16 - Implement value : Private - Implement source file : validation/core/integration_test/class_design_implementation/negative_variable_visibility_mismatch/transport.cpp - Implement source line : 14 - Required Action : Align the implementation with the unit design or update the unit design + [Implementation] Field variable "speed" visibility on entity "Engine" differs between the unit design and the C++ class implementation. + Entity : "Engine" + Field : variable "speed" visibility + Design value : Public + Design source file : "validation/core/integration_test/class_design_implementation/negative_variable_visibility_mismatch/class_diagram.puml" + Design source line : 16 + Implementation value : Private + Implementation source file : "validation/core/integration_test/class_design_implementation/negative_variable_visibility_mismatch/transport.cpp" + Implementation source line : 14 + Fix : Make variable "speed" visibility in entity "Engine" consistent between the unit design and the C++ class implementation. diff --git a/validation/core/integration_test/component_class/BUILD b/validation/core/integration_test/component_class/BUILD deleted file mode 100644 index a1642ff5..00000000 --- a/validation/core/integration_test/component_class/BUILD +++ /dev/null @@ -1,43 +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 -# ******************************************************************************* - -load("@rules_rust//rust:defs.bzl", "rust_test") - -filegroup( - name = "component_class_test_data", - srcs = [ - "//validation/core/integration_test/component_class/negative_boundary_mismatch:case_data", - "//validation/core/integration_test/component_class/negative_case_sensitive_mismatch:case_data", - "//validation/core/integration_test/component_class/negative_missing_namespace_coverage:case_data", - "//validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch:case_data", - "//validation/core/integration_test/component_class/positive_exact_match:case_data", - "//validation/core/integration_test/component_class/positive_multi_class_files:case_data", - "//validation/core/integration_test/component_class/positive_suffix_match:case_data", - ], -) - -rust_test( - name = "component_class_integration_test", - srcs = ["component_class_suite.rs"], - crate_root = "component_class_suite.rs", - data = [ - ":component_class_test_data", - ], - deps = [ - "//tools/metamodel/class:class_diagram", - "//validation/core:validation_cli", - "//validation/core/integration_test:test_framework", - "@crates//:serde", - "@crates//:serde_json", - ], -) diff --git a/validation/core/integration_test/component_class/component_class_suite.rs b/validation/core/integration_test/component_class/component_class_suite.rs deleted file mode 100644 index 21904a01..00000000 --- a/validation/core/integration_test/component_class/component_class_suite.rs +++ /dev/null @@ -1,92 +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 -// -// -// SPDX-License-Identifier: Apache-2.0 -// ******************************************************************************* - -use test_framework::{ - assert_cli_result, collect_case_fbs_files, load_expected_fixture, run_validation_profile, - CliRunResult, -}; - -const SUITE_DIR: &str = "component_class"; - -fn run_case_from_cli( - case_dir: &str, - component_fbs_paths: &[String], - class_fbs_paths: &[String], -) -> CliRunResult { - run_validation_profile( - &format!("component_class_{case_dir}"), - "architectural-design", - serde_json::json!({ - "component_diagrams": component_fbs_paths, - "class_diagrams": class_fbs_paths, - }), - ) -} - -fn assert_case(case_dir: &str) { - let expected = load_expected_fixture(SUITE_DIR, case_dir); - let component_fbs_paths = collect_case_fbs_files(SUITE_DIR, case_dir, "component"); - let class_fbs_paths = collect_case_fbs_files(SUITE_DIR, case_dir, "class"); - - let result = if !component_fbs_paths.is_empty() && !class_fbs_paths.is_empty() { - run_case_from_cli(case_dir, &component_fbs_paths, &class_fbs_paths) - } else { - panic!( - "missing generated FBS fixtures for {case_dir}: expected at least one component_diagram*.fbs.bin and class_diagram*.fbs.bin", - ); - }; - - assert_cli_result(case_dir, &expected, &result); -} - -#[test] -#[ignore = "component-class validation profile is pending"] -fn positive_exact_match_suite_case() { - assert_case("positive_exact_match"); -} - -#[test] -#[ignore = "component-class validation profile is pending"] -fn positive_suffix_suite_case() { - assert_case("positive_suffix_match"); -} - -#[test] -#[ignore = "component-class validation profile is pending"] -fn positive_multi_class_files_suite_case() { - assert_case("positive_multi_class_files"); -} - -#[test] -#[ignore = "component-class validation profile is pending"] -fn negative_missing_namespace_coverage_suite_case() { - assert_case("negative_missing_namespace_coverage"); -} - -#[test] -#[ignore = "component-class validation profile is pending"] -fn negative_boundary_mismatch_suite_case() { - assert_case("negative_boundary_mismatch"); -} - -#[test] -#[ignore = "component-class validation profile is pending"] -fn negative_case_sensitive_mismatch_suite_case() { - assert_case("negative_case_sensitive_mismatch"); -} - -#[test] -#[ignore = "component-class validation profile is pending"] -fn negative_multi_class_files_with_mismatch_suite_case() { - assert_case("negative_multi_class_files_with_mismatch"); -} diff --git a/validation/core/integration_test/component_class/negative_boundary_mismatch/expected.json b/validation/core/integration_test/component_class/negative_boundary_mismatch/expected.json deleted file mode 100644 index 527240c2..00000000 --- a/validation/core/integration_test/component_class/negative_boundary_mismatch/expected.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "no enclosing namespace ID suffix match for component unit ID", - "package_a.component_a.unit_1", - "enclosing namespace ID is not a suffix of any component unit ID", - "Namespace ID : \"it_1\"" - ] -} diff --git a/validation/core/integration_test/component_class/negative_case_sensitive_mismatch/expected.json b/validation/core/integration_test/component_class/negative_case_sensitive_mismatch/expected.json deleted file mode 100644 index 720e1a79..00000000 --- a/validation/core/integration_test/component_class/negative_case_sensitive_mismatch/expected.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "no enclosing namespace ID suffix match for component unit ID", - "package_a.component_a.Unit_1", - "enclosing namespace ID is not a suffix of any component unit ID", - "Namespace ID : \"unit_1\"" - ] -} diff --git a/validation/core/integration_test/component_class/negative_missing_namespace_coverage/expected.json b/validation/core/integration_test/component_class/negative_missing_namespace_coverage/expected.json deleted file mode 100644 index 38f90dfc..00000000 --- a/validation/core/integration_test/component_class/negative_missing_namespace_coverage/expected.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "no enclosing namespace ID suffix match for component unit ID", - "package_a.component_a.unit_1", - "enclosing namespace ID is not a suffix of any component unit ID", - "Namespace ID : \"unit_2\"" - ] -} diff --git a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/component_diagram_part2.puml b/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/component_diagram_part2.puml deleted file mode 100644 index 8a6a0922..00000000 --- a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/component_diagram_part2.puml +++ /dev/null @@ -1,22 +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 -' ******************************************************************************* - -@startuml component_diagram - -package "Package B" as package_b { - component "Component B" as component_b <> { - component "Unit 2" as unit_2 <> - } -} - -@enduml diff --git a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/expected.json b/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/expected.json deleted file mode 100644 index ba1ae367..00000000 --- a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/expected.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "no enclosing namespace ID suffix match for component unit ID", - "package_b.component_b.unit_2", - "enclosing namespace ID is not a suffix of any component unit ID", - "Namespace ID : \"unit_3\"" - ] -} diff --git a/validation/core/integration_test/component_class/positive_exact_match/class_diagram.puml b/validation/core/integration_test/component_class/positive_exact_match/class_diagram.puml deleted file mode 100644 index 147e6344..00000000 --- a/validation/core/integration_test/component_class/positive_exact_match/class_diagram.puml +++ /dev/null @@ -1,39 +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 -' ******************************************************************************* - -@startuml class_diagram - -namespace package_a::component_a::unit_1 { - class Foo { - - {final} - -- - + GetNumber() : uint8_t - + SetNumber(value : uint8_t) : void - } -} - -namespace package_a::component_a::unit_2 { - class Bar { - {final} - -- - - foo_ : unique_ptr - -- - + Bar(foo : unique_ptr) - + AssertNumber() : bool - } -} - -Bar --> Foo : uses - -@enduml diff --git a/validation/core/integration_test/component_class/positive_exact_match/component_diagram.puml b/validation/core/integration_test/component_class/positive_exact_match/component_diagram.puml deleted file mode 100644 index a678fc4d..00000000 --- a/validation/core/integration_test/component_class/positive_exact_match/component_diagram.puml +++ /dev/null @@ -1,23 +0,0 @@ -' ******************************************************************************* -' Copyright (c) 2025 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 -' ******************************************************************************* - -@startuml component_diagram - -package "Package A" as package_a { - component "Component A" as component_a <> { - component "Unit 1" as unit_1 <> - component "Unit 2" as unit_2 <> - } -} - -@enduml diff --git a/validation/core/integration_test/component_class/positive_exact_match/expected.json b/validation/core/integration_test/component_class/positive_exact_match/expected.json deleted file mode 100644 index 208a55e1..00000000 --- a/validation/core/integration_test/component_class/positive_exact_match/expected.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "should_pass": true, - "error_contains": [] -} diff --git a/validation/core/integration_test/component_class/positive_multi_class_files/BUILD b/validation/core/integration_test/component_class/positive_multi_class_files/BUILD deleted file mode 100644 index 42557d66..00000000 --- a/validation/core/integration_test/component_class/positive_multi_class_files/BUILD +++ /dev/null @@ -1,48 +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 -# ******************************************************************************* - -load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design", "unit_design") -load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") - -architectural_design( - name = "component_design", - static = ["component_diagram.puml"], - visibility = ["//visibility:private"], -) - -unit_design( - name = "class_design", - static = [ - "class_diagram_part1.puml", - "class_diagram_part2.puml", - ], - visibility = ["//visibility:private"], -) - -provider_fbs_fixture_bundle( - name = "fbs", - visibility = ["//visibility:public"], - deps = [ - ":class_design", - ":component_design", - ], -) - -filegroup( - name = "case_data", - srcs = [ - "expected.json", - ":fbs", - ], - visibility = ["//visibility:public"], -) diff --git a/validation/core/integration_test/component_class/positive_multi_class_files/expected.json b/validation/core/integration_test/component_class/positive_multi_class_files/expected.json deleted file mode 100644 index 208a55e1..00000000 --- a/validation/core/integration_test/component_class/positive_multi_class_files/expected.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "should_pass": true, - "error_contains": [] -} diff --git a/validation/core/integration_test/component_class/positive_suffix_match/BUILD b/validation/core/integration_test/component_class/positive_suffix_match/BUILD deleted file mode 100644 index c19d1abb..00000000 --- a/validation/core/integration_test/component_class/positive_suffix_match/BUILD +++ /dev/null @@ -1,46 +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 -# ******************************************************************************* - -load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design", "unit_design") -load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") - -architectural_design( - name = "component_design", - static = ["component_diagram.puml"], - visibility = ["//visibility:private"], -) - -unit_design( - name = "class_design", - static = ["class_diagram.puml"], - visibility = ["//visibility:private"], -) - -provider_fbs_fixture_bundle( - name = "fbs", - output_root = "", - visibility = ["//visibility:public"], - deps = [ - ":class_design", - ":component_design", - ], -) - -filegroup( - name = "case_data", - srcs = [ - "expected.json", - ":fbs", - ], - visibility = ["//visibility:public"], -) diff --git a/validation/core/integration_test/component_class/positive_suffix_match/class_diagram.puml b/validation/core/integration_test/component_class/positive_suffix_match/class_diagram.puml deleted file mode 100644 index 4604a05a..00000000 --- a/validation/core/integration_test/component_class/positive_suffix_match/class_diagram.puml +++ /dev/null @@ -1,28 +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 -' ******************************************************************************* - -@startuml class_diagram - -namespace unit_1 { - class Foo { - + GetNumber() : uint8_t - } -} - -namespace unit_2 { - class Bar { - + AssertNumber() : bool - } -} - -@enduml diff --git a/validation/core/integration_test/component_class/positive_suffix_match/expected.json b/validation/core/integration_test/component_class/positive_suffix_match/expected.json deleted file mode 100644 index 208a55e1..00000000 --- a/validation/core/integration_test/component_class/positive_suffix_match/expected.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "should_pass": true, - "error_contains": [] -} diff --git a/validation/core/integration_test/component_internal_api/BUILD b/validation/core/integration_test/component_internal_api/BUILD index 6a5e3d98..f9521319 100644 --- a/validation/core/integration_test/component_internal_api/BUILD +++ b/validation/core/integration_test/component_internal_api/BUILD @@ -16,6 +16,10 @@ load("@rules_rust//rust:defs.bzl", "rust_test") filegroup( name = "component_internal_api_test_data", srcs = [ + "//validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded:case_data", + "//validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded:case_data", + "//validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded:case_data", + "//validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded:case_data", "//validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api:case_data", "//validation/core/integration_test/component_internal_api/positive_interface_match:case_data", ], diff --git a/validation/core/integration_test/component_internal_api/component_internal_api_suite.rs b/validation/core/integration_test/component_internal_api/component_internal_api_suite.rs index 47ef38d6..cec6b424 100644 --- a/validation/core/integration_test/component_internal_api/component_internal_api_suite.rs +++ b/validation/core/integration_test/component_internal_api/component_internal_api_suite.rs @@ -12,8 +12,8 @@ // ******************************************************************************* use test_framework::{ - assert_cli_result, collect_case_fbs_files, load_expected_fixture, run_validation_profile, - CliRunResult, + assert_cli_result, collect_case_fbs_files, load_expected_yaml_fixture, normalize_yaml_result, + run_validation_profile, CliRunResult, }; const SUITE_DIR: &str = "component_internal_api"; @@ -34,7 +34,7 @@ fn run_case_from_cli( } fn assert_case(case_dir: &str) { - let expected = load_expected_fixture(SUITE_DIR, case_dir); + let expected = load_expected_yaml_fixture(SUITE_DIR, case_dir); let component_fbs_paths = collect_case_fbs_files(SUITE_DIR, case_dir, "component"); let internal_api_fbs_paths = collect_case_fbs_files(SUITE_DIR, case_dir, "internal_api"); @@ -46,6 +46,8 @@ fn assert_case(case_dir: &str) { ); }; + let result = normalize_yaml_result(result); + assert_cli_result(case_dir, &expected, &result); } @@ -54,6 +56,26 @@ fn negative_interface_missing_from_internal_api_suite_case() { assert_case("negative_interface_missing_from_internal_api"); } +#[test] +fn negative_duplicate_unit_alias_casefolded_suite_case() { + assert_case("negative_duplicate_unit_alias_casefolded"); +} + +#[test] +fn negative_duplicate_component_alias_casefolded_suite_case() { + assert_case("negative_duplicate_component_alias_casefolded"); +} + +#[test] +fn negative_duplicate_interface_alias_casefolded_suite_case() { + assert_case("negative_duplicate_interface_alias_casefolded"); +} + +#[test] +fn negative_duplicate_dependable_element_alias_casefolded_suite_case() { + assert_case("negative_duplicate_dependable_element_alias_casefolded"); +} + #[test] fn positive_interface_match_suite_case() { assert_case("positive_interface_match"); diff --git a/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/BUILD b/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/BUILD new file mode 100644 index 00000000..26b6054f --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/BUILD @@ -0,0 +1,38 @@ +# ******************************************************************************* +# 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("//bazel/rules/rules_score:rules_score.bzl", "architectural_design") +load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") + +architectural_design( + name = "design", + internal_api = ["internal_api_diagram.puml"], + maturity = "development", + static = ["component_diagram.puml"], + visibility = ["//visibility:private"], +) + +provider_fbs_fixture_bundle( + name = "fbs", + visibility = ["//visibility:public"], + deps = [":design"], +) + +filegroup( + name = "case_data", + srcs = [ + "expected.yaml", + ":fbs", + ], + visibility = ["//visibility:public"], +) diff --git a/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/component_diagram.puml b/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/component_diagram.puml new file mode 100644 index 00000000..08f1ed79 --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/component_diagram.puml @@ -0,0 +1,23 @@ +' ******************************************************************************* +' 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 +' ******************************************************************************* + +@startuml component_diagram + +package "Package A" as package_a { + component "Component A" as component_a <> + component "Component A Shadow" as COMPONENT_A <> + + interface "Internal Interface" as iface_a +} + +@enduml diff --git a/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/expected.yaml b/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/expected.yaml new file mode 100644 index 00000000..5bf3a78f --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/expected.yaml @@ -0,0 +1,22 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Design] Component "component_a" is defined more than once in the component diagram. + Component : "component_a" + Parent : package_a + Component source file : "validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/component_diagram.puml" + Component source line : 17 + Duplicate source file : "validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/component_diagram.puml" + Duplicate source line : 18 + Fix : Keep only one component "component_a" under "package_a", or rename one of the duplicate entities. diff --git a/validation/core/integration_test/component_class/negative_boundary_mismatch/class_diagram.puml b/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/internal_api_diagram.puml similarity index 83% rename from validation/core/integration_test/component_class/negative_boundary_mismatch/class_diagram.puml rename to validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/internal_api_diagram.puml index 96370fb6..1e662a82 100644 --- a/validation/core/integration_test/component_class/negative_boundary_mismatch/class_diagram.puml +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_component_alias_casefolded/internal_api_diagram.puml @@ -11,11 +11,11 @@ ' SPDX-License-Identifier: Apache-2.0 ' ******************************************************************************* -@startuml class_diagram +@startuml internal_api_diagram -namespace it_1 { - class Foo { - + GetNumber() : uint8_t +package package_a { + interface "Internal Interface" as iface_a { + + GetData() } } diff --git a/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/BUILD b/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/BUILD new file mode 100644 index 00000000..26b6054f --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/BUILD @@ -0,0 +1,38 @@ +# ******************************************************************************* +# 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("//bazel/rules/rules_score:rules_score.bzl", "architectural_design") +load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") + +architectural_design( + name = "design", + internal_api = ["internal_api_diagram.puml"], + maturity = "development", + static = ["component_diagram.puml"], + visibility = ["//visibility:private"], +) + +provider_fbs_fixture_bundle( + name = "fbs", + visibility = ["//visibility:public"], + deps = [":design"], +) + +filegroup( + name = "case_data", + srcs = [ + "expected.yaml", + ":fbs", + ], + visibility = ["//visibility:public"], +) diff --git a/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/component_diagram.puml b/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/component_diagram.puml new file mode 100644 index 00000000..bc976285 --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/component_diagram.puml @@ -0,0 +1,22 @@ +' ******************************************************************************* +' 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 +' ******************************************************************************* + +@startuml component_diagram + +package "Seooc A" as seooc_a <> { + interface "Internal Interface" as iface_a +} + +package "Seooc A Shadow" as SEOOC_A <> + +@enduml diff --git a/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/expected.yaml b/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/expected.yaml new file mode 100644 index 00000000..b4d6499d --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/expected.yaml @@ -0,0 +1,22 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Design] Dependable element "seooc_a" is defined more than once in the component diagram. + Dependable element : "seooc_a" + Parent : + Component source file : "validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/component_diagram.puml" + Component source line : 16 + Duplicate source file : "validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/component_diagram.puml" + Duplicate source line : 20 + Fix : Keep only one dependable element "seooc_a" under "", or rename one of the duplicate entities. diff --git a/validation/core/integration_test/component_class/negative_case_sensitive_mismatch/class_diagram.puml b/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/internal_api_diagram.puml similarity index 83% rename from validation/core/integration_test/component_class/negative_case_sensitive_mismatch/class_diagram.puml rename to validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/internal_api_diagram.puml index e8ab88f8..0d11d24d 100644 --- a/validation/core/integration_test/component_class/negative_case_sensitive_mismatch/class_diagram.puml +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_dependable_element_alias_casefolded/internal_api_diagram.puml @@ -11,11 +11,11 @@ ' SPDX-License-Identifier: Apache-2.0 ' ******************************************************************************* -@startuml class_diagram +@startuml internal_api_diagram -namespace unit_1 { - class Foo { - + GetNumber() : uint8_t +package seooc_a { + interface "Internal Interface" as iface_a { + + GetData() } } diff --git a/validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/BUILD b/validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/BUILD new file mode 100644 index 00000000..26b6054f --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/BUILD @@ -0,0 +1,38 @@ +# ******************************************************************************* +# 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("//bazel/rules/rules_score:rules_score.bzl", "architectural_design") +load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") + +architectural_design( + name = "design", + internal_api = ["internal_api_diagram.puml"], + maturity = "development", + static = ["component_diagram.puml"], + visibility = ["//visibility:private"], +) + +provider_fbs_fixture_bundle( + name = "fbs", + visibility = ["//visibility:public"], + deps = [":design"], +) + +filegroup( + name = "case_data", + srcs = [ + "expected.yaml", + ":fbs", + ], + visibility = ["//visibility:public"], +) diff --git a/validation/core/integration_test/component_class/negative_missing_namespace_coverage/component_diagram.puml b/validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/component_diagram.puml similarity index 85% rename from validation/core/integration_test/component_class/negative_missing_namespace_coverage/component_diagram.puml rename to validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/component_diagram.puml index 5fb313f0..9dbb726c 100644 --- a/validation/core/integration_test/component_class/negative_missing_namespace_coverage/component_diagram.puml +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/component_diagram.puml @@ -14,9 +14,8 @@ @startuml component_diagram package "Package A" as package_a { - component "Component A" as component_a <> { - component "Unit 1" as unit_1 <> - } + interface "Internal Interface" as iface_a + interface "Internal Interface Shadow" as IFACE_A } @enduml diff --git a/validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/expected.yaml b/validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/expected.yaml new file mode 100644 index 00000000..fc64da52 --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/expected.yaml @@ -0,0 +1,22 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Design] Interface "iface_a" is defined more than once in the component diagram. + Interface : "iface_a" + Parent : package_a + Component source file : "validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/component_diagram.puml" + Component source line : 17 + Duplicate source file : "validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/component_diagram.puml" + Duplicate source line : 18 + Fix : Keep only one interface "iface_a" under "package_a", or rename one of the duplicate entities. diff --git a/validation/core/integration_test/component_class/negative_missing_namespace_coverage/class_diagram.puml b/validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/internal_api_diagram.puml similarity index 83% rename from validation/core/integration_test/component_class/negative_missing_namespace_coverage/class_diagram.puml rename to validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/internal_api_diagram.puml index 0b5b760e..1e662a82 100644 --- a/validation/core/integration_test/component_class/negative_missing_namespace_coverage/class_diagram.puml +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded/internal_api_diagram.puml @@ -11,11 +11,11 @@ ' SPDX-License-Identifier: Apache-2.0 ' ******************************************************************************* -@startuml class_diagram +@startuml internal_api_diagram -namespace unit_2 { - class Bar { - + AssertNumber() : bool +package package_a { + interface "Internal Interface" as iface_a { + + GetData() } } diff --git a/validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/BUILD b/validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/BUILD new file mode 100644 index 00000000..26b6054f --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/BUILD @@ -0,0 +1,38 @@ +# ******************************************************************************* +# 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("//bazel/rules/rules_score:rules_score.bzl", "architectural_design") +load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") + +architectural_design( + name = "design", + internal_api = ["internal_api_diagram.puml"], + maturity = "development", + static = ["component_diagram.puml"], + visibility = ["//visibility:private"], +) + +provider_fbs_fixture_bundle( + name = "fbs", + visibility = ["//visibility:public"], + deps = [":design"], +) + +filegroup( + name = "case_data", + srcs = [ + "expected.yaml", + ":fbs", + ], + visibility = ["//visibility:public"], +) diff --git a/validation/core/integration_test/component_class/negative_boundary_mismatch/component_diagram.puml b/validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/component_diagram.puml similarity index 84% rename from validation/core/integration_test/component_class/negative_boundary_mismatch/component_diagram.puml rename to validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/component_diagram.puml index 5fb313f0..4205e1dc 100644 --- a/validation/core/integration_test/component_class/negative_boundary_mismatch/component_diagram.puml +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/component_diagram.puml @@ -16,7 +16,11 @@ package "Package A" as package_a { component "Component A" as component_a <> { component "Unit 1" as unit_1 <> + component "Unit 1 Shadow" as UNIT_1 <> } + + interface "InternalInterface" as InternalInterface + unit_1 -( InternalInterface } @enduml diff --git a/validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/expected.yaml b/validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/expected.yaml new file mode 100644 index 00000000..27c7b9dc --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/expected.yaml @@ -0,0 +1,22 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Design] Unit "unit_1" is defined more than once in the component diagram. + Unit : "unit_1" + Parent : component_a + Component source file : "validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/component_diagram.puml" + Component source line : 18 + Duplicate source file : "validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/component_diagram.puml" + Duplicate source line : 19 + Fix : Keep only one unit "unit_1" under "component_a", or rename one of the duplicate entities. diff --git a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/class_diagram_part1.puml b/validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/internal_api_diagram.puml similarity index 82% rename from validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/class_diagram_part1.puml rename to validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/internal_api_diagram.puml index e8ab88f8..f86b6817 100644 --- a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/class_diagram_part1.puml +++ b/validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded/internal_api_diagram.puml @@ -11,11 +11,11 @@ ' SPDX-License-Identifier: Apache-2.0 ' ******************************************************************************* -@startuml class_diagram +@startuml internal_api_diagram -namespace unit_1 { - class Foo { - + GetNumber() : uint8_t +package package_a { + interface "InternalInterface" as InternalInterface { + + GetData() } } diff --git a/validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/BUILD b/validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/BUILD index b1a89f2e..26b6054f 100644 --- a/validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/BUILD +++ b/validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/BUILD @@ -31,7 +31,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/expected.json b/validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/expected.json deleted file mode 100644 index fcc1f30a..00000000 --- a/validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/expected.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "Internal API consistency failure: Missing internal API interface", - "Missing interfaces : \"package_a.InternalInterface\"" - ] -} diff --git a/validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/expected.yaml b/validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/expected.yaml new file mode 100644 index 00000000..bc85250e --- /dev/null +++ b/validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/expected.yaml @@ -0,0 +1,19 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Interface] Component interface(s) "package_a.InternalInterface" from the component diagram not found in the internal API diagram. + Missing interfaces : "package_a.InternalInterface" + Component source file for "package_a.InternalInterface" : "validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api/component_diagram.puml" + Component source line for "package_a.InternalInterface" : 22 + Fix : Add interface declaration(s) "package_a.InternalInterface" in the internal API diagram, or remove those interface declarations from the component diagram. diff --git a/validation/core/integration_test/component_internal_api/negative_method_missing_from_internal_api/expected.json b/validation/core/integration_test/component_internal_api/negative_method_missing_from_internal_api/expected.json deleted file mode 100644 index fcc1f30a..00000000 --- a/validation/core/integration_test/component_internal_api/negative_method_missing_from_internal_api/expected.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "Internal API consistency failure: Missing internal API interface", - "Missing interfaces : \"package_a.InternalInterface\"" - ] -} diff --git a/validation/core/integration_test/component_internal_api/positive_interface_match/BUILD b/validation/core/integration_test/component_internal_api/positive_interface_match/BUILD index b7fa7290..017c5d79 100644 --- a/validation/core/integration_test/component_internal_api/positive_interface_match/BUILD +++ b/validation/core/integration_test/component_internal_api/positive_interface_match/BUILD @@ -30,7 +30,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/component_internal_api/positive_interface_match/expected.json b/validation/core/integration_test/component_internal_api/positive_interface_match/expected.json deleted file mode 100644 index 208a55e1..00000000 --- a/validation/core/integration_test/component_internal_api/positive_interface_match/expected.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "should_pass": true, - "error_contains": [] -} diff --git a/validation/core/integration_test/component_internal_api/positive_interface_match/expected.yaml b/validation/core/integration_test/component_internal_api/positive_interface_match/expected.yaml new file mode 100644 index 00000000..898ecba3 --- /dev/null +++ b/validation/core/integration_test/component_internal_api/positive_interface_match/expected.yaml @@ -0,0 +1,13 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: true diff --git a/validation/core/integration_test/component_public_api/negative_case_sensitive/expected.yaml b/validation/core/integration_test/component_public_api/negative_case_sensitive/expected.yaml index 92248f8a..3237997c 100644 --- a/validation/core/integration_test/component_public_api/negative_case_sensitive/expected.yaml +++ b/validation/core/integration_test/component_public_api/negative_case_sensitive/expected.yaml @@ -12,5 +12,8 @@ # ******************************************************************************* should_pass: false error_contains: | - Missing public APIs : "SampleLibraryAPI" - Action : Declare each public API interface in the public API class diagram or remove it from the component diagram + [Interface] Public API interface(s) "SampleLibraryAPI" from the static diagram not found in the public API diagram; use "SampleLibraryAPI" (case-sensitive). + Missing public APIs : "SampleLibraryAPI" + Static source file for "SampleLibraryAPI" : "validation/core/integration_test/component_public_api/negative_case_sensitive/static_design.puml" + Static source line for "SampleLibraryAPI" : 24 + Fix : Add public API declaration(s) "SampleLibraryAPI" in the public API diagram, or remove those interface declarations from the static diagram; if already declared, use "SampleLibraryAPI" (case-sensitive). diff --git a/validation/core/integration_test/component_public_api/negative_public_api_lack_of_relationship/expected.yaml b/validation/core/integration_test/component_public_api/negative_public_api_lack_of_relationship/expected.yaml index a22f809e..3b89406f 100644 --- a/validation/core/integration_test/component_public_api/negative_public_api_lack_of_relationship/expected.yaml +++ b/validation/core/integration_test/component_public_api/negative_public_api_lack_of_relationship/expected.yaml @@ -12,6 +12,8 @@ # ******************************************************************************* should_pass: false error_contains: | - Public API interface has no component relationship: - Public APIs : "SampleLibraryAPI" - Action : Connect each public API interface to the SEooC, or remove it from the static design diagram + [Interface] Public API interface(s) "SampleLibraryAPI" in the static diagram have no relationship to the SEooC. + Public APIs : "SampleLibraryAPI" + Static source file for "SampleLibraryAPI" : "validation/core/integration_test/component_public_api/negative_public_api_lack_of_relationship/static_design.puml" + Static source line for "SampleLibraryAPI" : 22 + Fix : Connect public API interface(s) "SampleLibraryAPI" from the SEooC boundary in the static diagram, or remove those interface declarations if they are not intended to be public. diff --git a/validation/core/integration_test/component_public_api/negative_public_api_missing/expected.yaml b/validation/core/integration_test/component_public_api/negative_public_api_missing/expected.yaml index 03b8d59d..bfa2882c 100644 --- a/validation/core/integration_test/component_public_api/negative_public_api_missing/expected.yaml +++ b/validation/core/integration_test/component_public_api/negative_public_api_missing/expected.yaml @@ -12,5 +12,10 @@ # ******************************************************************************* should_pass: false error_contains: | - Missing public APIs : "SampleLibraryAPI1", "SampleLibraryAPI2" - Action : Declare each public API interface in the public API class diagram or remove it from the component diagram + [Interface] Public API interface(s) "SampleLibraryAPI1", "SampleLibraryAPI2" from the static diagram not found in the public API diagram. + Missing public APIs : "SampleLibraryAPI1", "SampleLibraryAPI2" + Static source file for "SampleLibraryAPI1" : "validation/core/integration_test/component_public_api/negative_public_api_missing/static_design.puml" + Static source line for "SampleLibraryAPI1" : 24 + Static source file for "SampleLibraryAPI2" : "validation/core/integration_test/component_public_api/negative_public_api_missing/static_design.puml" + Static source line for "SampleLibraryAPI2" : 25 + Fix : Add public API declaration(s) "SampleLibraryAPI1", "SampleLibraryAPI2" in the public API diagram, or remove those interface declarations from the static diagram. diff --git a/validation/core/integration_test/component_public_api/negative_public_api_wrong_type/expected.yaml b/validation/core/integration_test/component_public_api/negative_public_api_wrong_type/expected.yaml index 92248f8a..cf62042a 100644 --- a/validation/core/integration_test/component_public_api/negative_public_api_wrong_type/expected.yaml +++ b/validation/core/integration_test/component_public_api/negative_public_api_wrong_type/expected.yaml @@ -12,5 +12,8 @@ # ******************************************************************************* should_pass: false error_contains: | - Missing public APIs : "SampleLibraryAPI" - Action : Declare each public API interface in the public API class diagram or remove it from the component diagram + [Interface] Public API interface(s) "SampleLibraryAPI" from the static diagram not found in the public API diagram. + Missing public APIs : "SampleLibraryAPI" + Static source file for "SampleLibraryAPI" : "validation/core/integration_test/component_public_api/negative_public_api_wrong_type/static_design.puml" + Static source line for "SampleLibraryAPI" : 22 + Fix : Add public API declaration(s) "SampleLibraryAPI" in the public API diagram, or remove those interface declarations from the static diagram. diff --git a/validation/core/integration_test/component_public_api/negative_public_api_wrong_type/static_design.puml b/validation/core/integration_test/component_public_api/negative_public_api_wrong_type/static_design.puml index ae7936ad..47e86351 100644 --- a/validation/core/integration_test/component_public_api/negative_public_api_wrong_type/static_design.puml +++ b/validation/core/integration_test/component_public_api/negative_public_api_wrong_type/static_design.puml @@ -20,6 +20,6 @@ package "Sample Seooc" as sample_seooc <> { } interface "SampleLibraryAPI" as SampleLibraryAPI -component_example )-d- SampleLibraryAPI +sample_seooc )-d- SampleLibraryAPI @enduml diff --git a/validation/core/integration_test/component_sequence/BUILD b/validation/core/integration_test/component_sequence/BUILD index 8bd87983..05088387 100644 --- a/validation/core/integration_test/component_sequence/BUILD +++ b/validation/core/integration_test/component_sequence/BUILD @@ -23,6 +23,8 @@ filegroup( "//validation/core/integration_test/component_sequence/negative_mixed_mismatch:case_data", "//validation/core/integration_test/component_sequence/negative_orphan_participant:case_data", "//validation/core/integration_test/component_sequence/positive_exact_match:case_data", + "//validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return:case_data", + "//validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection:case_data", ], ) diff --git a/validation/core/integration_test/component_sequence/component_sequence_suite.rs b/validation/core/integration_test/component_sequence/component_sequence_suite.rs index 6676b265..7abd4db4 100644 --- a/validation/core/integration_test/component_sequence/component_sequence_suite.rs +++ b/validation/core/integration_test/component_sequence/component_sequence_suite.rs @@ -12,8 +12,8 @@ // ******************************************************************************* use test_framework::{ - assert_cli_result, collect_case_fbs_files, load_expected_fixture, run_validation_profile, - CliRunResult, + assert_cli_result, collect_case_fbs_files, load_expected_yaml_fixture, normalize_yaml_result, + run_validation_profile, CliRunResult, }; const SUITE_DIR: &str = "component_sequence"; @@ -34,7 +34,7 @@ fn run_case_from_cli( } fn assert_case(case_dir: &str) { - let expected = load_expected_fixture(SUITE_DIR, case_dir); + let expected = load_expected_yaml_fixture(SUITE_DIR, case_dir); let component_fbs_paths = collect_case_fbs_files(SUITE_DIR, case_dir, "component"); let sequence_fbs_paths = collect_case_fbs_files(SUITE_DIR, case_dir, "sequence"); @@ -46,6 +46,8 @@ fn assert_case(case_dir: &str) { ); }; + let result = normalize_yaml_result(result); + assert_cli_result(case_dir, &expected, &result); } @@ -59,6 +61,16 @@ fn negative_missing_participant_suite_case() { assert_case("negative_missing_participant"); } +#[test] +fn positive_external_caller_in_sequence_connection_suite_case() { + assert_case("positive_external_caller_in_sequence_connection"); +} + +#[test] +fn positive_external_callee_in_sequence_return_suite_case() { + assert_case("positive_external_callee_in_sequence_return"); +} + #[test] fn negative_missing_interface_connection_for_sequence_connected_units_suite_case() { assert_case("negative_missing_interface_connection_for_sequence_connected_units"); diff --git a/validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/BUILD b/validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/BUILD index 68419fff..86cd716f 100644 --- a/validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/BUILD +++ b/validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/BUILD @@ -31,7 +31,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/expected.json b/validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/expected.json deleted file mode 100644 index 8c3110f6..00000000 --- a/validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/expected.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "sequence-connected units have no corresponding shared interface connection in the component diagram", - "\"package_a.CallerInterface\"", - "\"package_a.CalleeInterface\"" - ] -} diff --git a/validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/expected.yaml b/validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/expected.yaml new file mode 100644 index 00000000..e0e0351a --- /dev/null +++ b/validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/expected.yaml @@ -0,0 +1,21 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Interface] Sequence-connected units "unit_1" and "unit_2" have no corresponding shared interface connection in the component diagram. + Unit pair : "unit_1" <-> "unit_2" + Sequence source file : "validation/core/integration_test/component_sequence/negative_missing_interface_connection_for_sequence_connected_units/sequence_diagram.puml" + Sequence source line : 19 + Interfaces for "unit_1" : "package_a.CallerInterface" + Interfaces for "unit_2" : "package_a.CalleeInterface" + Fix : Add a shared interface connection between "unit_1" and "unit_2" in the component diagram, or remove that function-call from the sequence diagram. diff --git a/validation/core/integration_test/component_sequence/negative_missing_participant/BUILD b/validation/core/integration_test/component_sequence/negative_missing_participant/BUILD index 68419fff..86cd716f 100644 --- a/validation/core/integration_test/component_sequence/negative_missing_participant/BUILD +++ b/validation/core/integration_test/component_sequence/negative_missing_participant/BUILD @@ -31,7 +31,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/component_sequence/negative_missing_participant/expected.json b/validation/core/integration_test/component_sequence/negative_missing_participant/expected.json deleted file mode 100644 index 43bfc4dc..00000000 --- a/validation/core/integration_test/component_sequence/negative_missing_participant/expected.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "component unit alias not found in sequence participants", - "\"unit_3\"" - ] -} diff --git a/validation/core/integration_test/component_sequence/negative_missing_participant/expected.yaml b/validation/core/integration_test/component_sequence/negative_missing_participant/expected.yaml new file mode 100644 index 00000000..a068742e --- /dev/null +++ b/validation/core/integration_test/component_sequence/negative_missing_participant/expected.yaml @@ -0,0 +1,28 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: + - | + [Naming] Alias "unit_3" from the component diagram not found in the sequence diagram. + Alias : "unit_3" + Component source file : "validation/core/integration_test/component_sequence/negative_missing_participant/component_diagram.puml" + Component source line : 20 + Fix : Add sequence participant "unit_3" in the sequence diagram, or remove it from the component diagram. + - | + [Interface] Sequence-connected units "unit_1" and "unit_2" have no corresponding shared interface connection in the component diagram. + Unit pair : "unit_1" <-> "unit_2" + Sequence source file : "validation/core/integration_test/component_sequence/negative_missing_participant/sequence_diagram.puml" + Sequence source line : 19 + Interfaces for "unit_1" : + Interfaces for "unit_2" : + Fix : Add a shared interface connection between "unit_1" and "unit_2" in the component diagram, or remove that function-call from the sequence diagram. diff --git a/validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/BUILD b/validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/BUILD index 68419fff..86cd716f 100644 --- a/validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/BUILD +++ b/validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/BUILD @@ -31,7 +31,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/expected.json b/validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/expected.json deleted file mode 100644 index 7a3d9729..00000000 --- a/validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/expected.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "interface-connected units are missing a sequence function-call connection", - "\"unit_1\"", - "\"unit_2\"", - "\"package_a.InternalInterface\"" - ] -} diff --git a/validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/expected.yaml b/validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/expected.yaml new file mode 100644 index 00000000..7983f68e --- /dev/null +++ b/validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/expected.yaml @@ -0,0 +1,22 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Interface] Component-connected units "unit_1" and "unit_2" have no corresponding function-call in the sequence diagram. + Unit pair : "unit_1" <-> "unit_2" + Component source file for "unit_1" : "validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/component_diagram.puml" + Component source line for "unit_1" : 18 + Component source file for "unit_2" : "validation/core/integration_test/component_sequence/negative_missing_sequence_interaction_for_interface_connected_units/component_diagram.puml" + Component source line for "unit_2" : 19 + Shared interfaces : "package_a.InternalInterface" + Fix : Add a function-call between "unit_1" and "unit_2" in the sequence diagram, or remove that shared interface connection from the component diagram. diff --git a/validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/BUILD b/validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/BUILD index 68419fff..86cd716f 100644 --- a/validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/BUILD +++ b/validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/BUILD @@ -31,7 +31,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/expected.json b/validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/expected.json deleted file mode 100644 index 4392dd67..00000000 --- a/validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/expected.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "sequence-connected units have no corresponding shared interface connection in the component diagram", - "\"unit_1\"", - "\"unit_2\"" - ] -} diff --git a/validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/expected.yaml b/validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/expected.yaml new file mode 100644 index 00000000..2c3d2afd --- /dev/null +++ b/validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/expected.yaml @@ -0,0 +1,21 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Interface] Sequence-connected units "unit_1" and "unit_2" have no corresponding shared interface connection in the component diagram. + Unit pair : "unit_1" <-> "unit_2" + Sequence source file : "validation/core/integration_test/component_sequence/negative_missing_unit_interface_relation/sequence_diagram.puml" + Sequence source line : 19 + Interfaces for "unit_1" : + Interfaces for "unit_2" : "package_a.InternalInterface" + Fix : Add a shared interface connection between "unit_1" and "unit_2" in the component diagram, or remove that function-call from the sequence diagram. diff --git a/validation/core/integration_test/component_sequence/negative_mixed_mismatch/BUILD b/validation/core/integration_test/component_sequence/negative_mixed_mismatch/BUILD index 68419fff..86cd716f 100644 --- a/validation/core/integration_test/component_sequence/negative_mixed_mismatch/BUILD +++ b/validation/core/integration_test/component_sequence/negative_mixed_mismatch/BUILD @@ -31,7 +31,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/component_sequence/negative_mixed_mismatch/expected.json b/validation/core/integration_test/component_sequence/negative_mixed_mismatch/expected.json deleted file mode 100644 index 6f3e7fbc..00000000 --- a/validation/core/integration_test/component_sequence/negative_mixed_mismatch/expected.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "component unit alias not found in sequence participants", - "sequence participant not found in component unit aliases", - "\"unit_1\"", - "\"unit_3\"" - ] -} diff --git a/validation/core/integration_test/component_sequence/negative_mixed_mismatch/expected.yaml b/validation/core/integration_test/component_sequence/negative_mixed_mismatch/expected.yaml new file mode 100644 index 00000000..0fe42931 --- /dev/null +++ b/validation/core/integration_test/component_sequence/negative_mixed_mismatch/expected.yaml @@ -0,0 +1,34 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: + - | + [Naming] Alias "unit_1" from the component diagram not found in the sequence diagram. + Alias : "unit_1" + Component source file : "validation/core/integration_test/component_sequence/negative_mixed_mismatch/component_diagram.puml" + Component source line : 18 + Fix : Add sequence participant "unit_1" in the sequence diagram, or remove it from the component diagram. + - | + [Naming] Participant "unit_3" from the sequence diagram not found in the component diagram. + Participant : "unit_3" + Sequence source file : "validation/core/integration_test/component_sequence/negative_mixed_mismatch/sequence_diagram.puml" + Sequence source line : 17 + Fix : Add component unit alias "unit_3" in the component diagram, or remove it from the sequence diagram. + - | + [Interface] Sequence-connected units "unit_2" and "unit_3" have no corresponding shared interface connection in the component diagram. + Unit pair : "unit_2" <-> "unit_3" + Sequence source file : "validation/core/integration_test/component_sequence/negative_mixed_mismatch/sequence_diagram.puml" + Sequence source line : 19 + Interfaces for "unit_2" : + Interfaces for "unit_3" : + Fix : Add a shared interface connection between "unit_2" and "unit_3" in the component diagram, or remove that function-call from the sequence diagram. diff --git a/validation/core/integration_test/component_sequence/negative_orphan_participant/BUILD b/validation/core/integration_test/component_sequence/negative_orphan_participant/BUILD index 68419fff..86cd716f 100644 --- a/validation/core/integration_test/component_sequence/negative_orphan_participant/BUILD +++ b/validation/core/integration_test/component_sequence/negative_orphan_participant/BUILD @@ -31,7 +31,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/component_sequence/negative_orphan_participant/expected.json b/validation/core/integration_test/component_sequence/negative_orphan_participant/expected.json deleted file mode 100644 index dec5aabc..00000000 --- a/validation/core/integration_test/component_sequence/negative_orphan_participant/expected.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "sequence participant not found in component unit aliases", - "\"unit_3\"" - ] -} diff --git a/validation/core/integration_test/component_sequence/negative_orphan_participant/expected.yaml b/validation/core/integration_test/component_sequence/negative_orphan_participant/expected.yaml new file mode 100644 index 00000000..d0c9ee70 --- /dev/null +++ b/validation/core/integration_test/component_sequence/negative_orphan_participant/expected.yaml @@ -0,0 +1,28 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: + - | + [Naming] Participant "unit_3" from the sequence diagram not found in the component diagram. + Participant : "unit_3" + Sequence source file : "validation/core/integration_test/component_sequence/negative_orphan_participant/sequence_diagram.puml" + Sequence source line : 17 + Fix : Add component unit alias "unit_3" in the component diagram, or remove it from the sequence diagram. + - | + [Interface] Sequence-connected units "unit_1" and "unit_3" have no corresponding shared interface connection in the component diagram. + Unit pair : "unit_1" <-> "unit_3" + Sequence source file : "validation/core/integration_test/component_sequence/negative_orphan_participant/sequence_diagram.puml" + Sequence source line : 19 + Interfaces for "unit_1" : + Interfaces for "unit_3" : + Fix : Add a shared interface connection between "unit_1" and "unit_3" in the component diagram, or remove that function-call from the sequence diagram. diff --git a/validation/core/integration_test/component_sequence/positive_exact_match/BUILD b/validation/core/integration_test/component_sequence/positive_exact_match/BUILD index 985db0e2..bc4845f7 100644 --- a/validation/core/integration_test/component_sequence/positive_exact_match/BUILD +++ b/validation/core/integration_test/component_sequence/positive_exact_match/BUILD @@ -30,7 +30,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/component_sequence/positive_exact_match/expected.json b/validation/core/integration_test/component_sequence/positive_exact_match/expected.json deleted file mode 100644 index 208a55e1..00000000 --- a/validation/core/integration_test/component_sequence/positive_exact_match/expected.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "should_pass": true, - "error_contains": [] -} diff --git a/validation/core/integration_test/component_sequence/positive_exact_match/expected.yaml b/validation/core/integration_test/component_sequence/positive_exact_match/expected.yaml new file mode 100644 index 00000000..898ecba3 --- /dev/null +++ b/validation/core/integration_test/component_sequence/positive_exact_match/expected.yaml @@ -0,0 +1,13 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: true diff --git a/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/BUILD b/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/BUILD new file mode 100644 index 00000000..86cd716f --- /dev/null +++ b/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/BUILD @@ -0,0 +1,38 @@ +# ******************************************************************************* +# 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("//bazel/rules/rules_score:rules_score.bzl", "architectural_design") +load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") + +architectural_design( + name = "design", + dynamic = ["sequence_diagram.puml"], + maturity = "development", + static = ["component_diagram.puml"], + visibility = ["//visibility:private"], +) + +provider_fbs_fixture_bundle( + name = "fbs", + visibility = ["//visibility:public"], + deps = [":design"], +) + +filegroup( + name = "case_data", + srcs = [ + "expected.yaml", + ":fbs", + ], + visibility = ["//visibility:public"], +) diff --git a/validation/core/integration_test/component_class/positive_multi_class_files/component_diagram.puml b/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/component_diagram.puml similarity index 86% rename from validation/core/integration_test/component_class/positive_multi_class_files/component_diagram.puml rename to validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/component_diagram.puml index fc7b05ef..c87543a4 100644 --- a/validation/core/integration_test/component_class/positive_multi_class_files/component_diagram.puml +++ b/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/component_diagram.puml @@ -18,6 +18,10 @@ package "Package A" as package_a { component "Unit 1" as unit_1 <> component "Unit 2" as unit_2 <> } + + interface "InternalInterface" as InternalInterface + unit_1 -( InternalInterface + unit_2 )- InternalInterface } @enduml diff --git a/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/expected.yaml b/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/expected.yaml new file mode 100644 index 00000000..898ecba3 --- /dev/null +++ b/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/expected.yaml @@ -0,0 +1,13 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: true diff --git a/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/sequence_diagram.puml b/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/sequence_diagram.puml new file mode 100644 index 00000000..072dcc9b --- /dev/null +++ b/validation/core/integration_test/component_sequence/positive_external_callee_in_sequence_return/sequence_diagram.puml @@ -0,0 +1,22 @@ +' ******************************************************************************* +' 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 +' ******************************************************************************* + +@startuml sequence_diagram + +participant "Unit 1" as unit_1 <> +participant "Unit 2" as unit_2 <> + +unit_1 -> unit_2 : GetData() +--> unit_1 : Ack + +@enduml diff --git a/validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/BUILD b/validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/BUILD new file mode 100644 index 00000000..86cd716f --- /dev/null +++ b/validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/BUILD @@ -0,0 +1,38 @@ +# ******************************************************************************* +# 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("//bazel/rules/rules_score:rules_score.bzl", "architectural_design") +load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle") + +architectural_design( + name = "design", + dynamic = ["sequence_diagram.puml"], + maturity = "development", + static = ["component_diagram.puml"], + visibility = ["//visibility:private"], +) + +provider_fbs_fixture_bundle( + name = "fbs", + visibility = ["//visibility:public"], + deps = [":design"], +) + +filegroup( + name = "case_data", + srcs = [ + "expected.yaml", + ":fbs", + ], + visibility = ["//visibility:public"], +) diff --git a/validation/core/integration_test/component_class/positive_suffix_match/component_diagram.puml b/validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/component_diagram.puml similarity index 94% rename from validation/core/integration_test/component_class/positive_suffix_match/component_diagram.puml rename to validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/component_diagram.puml index fc7b05ef..6f183692 100644 --- a/validation/core/integration_test/component_class/positive_suffix_match/component_diagram.puml +++ b/validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/component_diagram.puml @@ -15,7 +15,6 @@ package "Package A" as package_a { component "Component A" as component_a <> { - component "Unit 1" as unit_1 <> component "Unit 2" as unit_2 <> } } diff --git a/validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/expected.yaml b/validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/expected.yaml new file mode 100644 index 00000000..898ecba3 --- /dev/null +++ b/validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/expected.yaml @@ -0,0 +1,13 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: true diff --git a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/class_diagram_part2.puml b/validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/sequence_diagram.puml similarity index 86% rename from validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/class_diagram_part2.puml rename to validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/sequence_diagram.puml index 0e3a00b3..07bb911c 100644 --- a/validation/core/integration_test/component_class/negative_multi_class_files_with_mismatch/class_diagram_part2.puml +++ b/validation/core/integration_test/component_sequence/positive_external_caller_in_sequence_connection/sequence_diagram.puml @@ -11,12 +11,10 @@ ' SPDX-License-Identifier: Apache-2.0 ' ******************************************************************************* -@startuml class_diagram +@startuml sequence_diagram -namespace unit_3 { - class Baz { - + Run() : void - } -} +participant "Unit 2" as unit_2 <> + +-> unit_2 : GetData() @enduml diff --git a/validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/BUILD b/validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/BUILD index 02461e67..47e01dfb 100644 --- a/validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/BUILD +++ b/validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/BUILD @@ -32,7 +32,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/expected.json b/validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/expected.json deleted file mode 100644 index 61784c19..00000000 --- a/validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/expected.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "internal API interface functions are not exercised in sequence diagrams", - "\"package_a.InternalInterface\"", - "\"SetData\"" - ] -} diff --git a/validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/expected.yaml b/validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/expected.yaml new file mode 100644 index 00000000..6e14df3a --- /dev/null +++ b/validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/expected.yaml @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Coverage] Methods "SetData" declared on internal API interface "package_a.InternalInterface" in the internal API diagram are not exercised in the sequence diagram. + Interface id : "package_a.InternalInterface" + Internal API source file : "validation/core/integration_test/sequence_internal_api/negative_interface_function_not_exercised/internal_api_diagram.puml" + Internal API source line : 17 + Missing functions : "SetData" + Fix : Add sequence interactions for functions "SetData" in the sequence diagram, or remove function declarations "SetData" in internal API interface "package_a.InternalInterface". diff --git a/validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/BUILD b/validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/BUILD index 02461e67..47e01dfb 100644 --- a/validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/BUILD +++ b/validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/BUILD @@ -32,7 +32,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/expected.json b/validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/expected.json deleted file mode 100644 index 9f98fc3e..00000000 --- a/validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/expected.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "sequence interaction does not match consumer/provider roles in the component diagram", - "Sequence call : \"unit_2\" -> \"unit_1\" : \"SendSignal\"", - "Expected caller role: \"unit_2\" should require shared interface(s) \"package_a.InternalInterface\"", - "Expected callee role: \"unit_1\" should provide shared interface(s) \"package_a.InternalInterface\"" - ] -} diff --git a/validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/expected.yaml b/validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/expected.yaml new file mode 100644 index 00000000..d972d6b0 --- /dev/null +++ b/validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/expected.yaml @@ -0,0 +1,21 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Interface] Sequence call "unit_2" -> "unit_1" : "SendSignal" in the sequence diagram does not match the required/provided interface roles in the component diagram. + Sequence call : "unit_2" -> "unit_1" : "SendSignal" + Sequence source file : "validation/core/integration_test/sequence_internal_api/negative_invalid_consumer_provider_direction/sequence_diagram.puml" + Sequence source line : 19 + Expected caller role : "unit_2" should require shared interface(s) "package_a.InternalInterface" + Expected callee role : "unit_1" should provide shared interface(s) "package_a.InternalInterface" + Fix : Add required/provided interface bindings for shared interface(s) "package_a.InternalInterface" in the component diagram, or remove sequence call "unit_2" -> "unit_1" : "SendSignal" in the sequence diagram. diff --git a/validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/BUILD b/validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/BUILD index 02461e67..47e01dfb 100644 --- a/validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/BUILD +++ b/validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/BUILD @@ -32,7 +32,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/expected.json b/validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/expected.json deleted file mode 100644 index a9ee21be..00000000 --- a/validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/expected.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "Method consistency failure", - "sequence function name was not found in the related interface methods", - "\"GetData\"" - ] -} diff --git a/validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/expected.yaml b/validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/expected.yaml new file mode 100644 index 00000000..f66dcff6 --- /dev/null +++ b/validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/expected.yaml @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: | + [Method] Sequence function "GetData" from sequence call "unit_1" -> "unit_2" : "GetData" in the sequence diagram not found in the internal API diagram. + Sequence call : "unit_1" -> "unit_2" : "GetData" + Sequence source file : "validation/core/integration_test/sequence_internal_api/negative_method_available_but_not_on_related_interface/sequence_diagram.puml" + Sequence source line : 19 + Detail : sequence function name was not found in the related interface methods + Fix : Add method "GetData" in a matching internal API interface in the internal API diagram, or remove sequence function "GetData" in sequence call "unit_1" -> "unit_2" : "GetData" in the sequence diagram. diff --git a/validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/BUILD b/validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/BUILD index 02461e67..47e01dfb 100644 --- a/validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/BUILD +++ b/validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/BUILD @@ -32,7 +32,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/expected.json b/validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/expected.json deleted file mode 100644 index 737cce5d..00000000 --- a/validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/expected.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "should_pass": false, - "error_contains": [ - "Method consistency failure", - "sequence function name was not found in available interface methods", - "\"GetData\"", - "\"package_a.InternalInterface\"" - ] -} diff --git a/validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/expected.yaml b/validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/expected.yaml new file mode 100644 index 00000000..5134e101 --- /dev/null +++ b/validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/expected.yaml @@ -0,0 +1,28 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: false +error_contains: + - | + [Method] Sequence function "GetData" from sequence call "unit_1" -> "unit_2" : "GetData" in the sequence diagram not found in the internal API diagram. + Sequence call : "unit_1" -> "unit_2" : "GetData" + Sequence source file : "validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/sequence_diagram.puml" + Sequence source line : 19 + Detail : sequence function name was not found in available interface methods + Fix : Add method "GetData" in a matching internal API interface in the internal API diagram, or remove sequence function "GetData" in sequence call "unit_1" -> "unit_2" : "GetData" in the sequence diagram. + - | + [Coverage] Methods "OtherMethod" declared on internal API interface "package_a.InternalInterface" in the internal API diagram are not exercised in the sequence diagram. + Interface id : "package_a.InternalInterface" + Internal API source file : "validation/core/integration_test/sequence_internal_api/negative_missing_method_in_related_interface/internal_api_diagram.puml" + Internal API source line : 17 + Missing functions : "OtherMethod" + Fix : Add sequence interactions for functions "OtherMethod" in the sequence diagram, or remove function declarations "OtherMethod" in internal API interface "package_a.InternalInterface". diff --git a/validation/core/integration_test/sequence_internal_api/positive_internal_api_method_match/BUILD b/validation/core/integration_test/sequence_internal_api/positive_internal_api_method_match/BUILD index d28d0ffb..09d46caf 100644 --- a/validation/core/integration_test/sequence_internal_api/positive_internal_api_method_match/BUILD +++ b/validation/core/integration_test/sequence_internal_api/positive_internal_api_method_match/BUILD @@ -31,7 +31,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/sequence_internal_api/positive_internal_api_method_match/expected.json b/validation/core/integration_test/sequence_internal_api/positive_internal_api_method_match/expected.json deleted file mode 100644 index 208a55e1..00000000 --- a/validation/core/integration_test/sequence_internal_api/positive_internal_api_method_match/expected.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "should_pass": true, - "error_contains": [] -} diff --git a/validation/core/integration_test/sequence_internal_api/positive_internal_api_method_match/expected.yaml b/validation/core/integration_test/sequence_internal_api/positive_internal_api_method_match/expected.yaml new file mode 100644 index 00000000..898ecba3 --- /dev/null +++ b/validation/core/integration_test/sequence_internal_api/positive_internal_api_method_match/expected.yaml @@ -0,0 +1,13 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: true diff --git a/validation/core/integration_test/sequence_internal_api/positive_self_call_method_match/BUILD b/validation/core/integration_test/sequence_internal_api/positive_self_call_method_match/BUILD index d28d0ffb..09d46caf 100644 --- a/validation/core/integration_test/sequence_internal_api/positive_self_call_method_match/BUILD +++ b/validation/core/integration_test/sequence_internal_api/positive_self_call_method_match/BUILD @@ -31,7 +31,7 @@ provider_fbs_fixture_bundle( filegroup( name = "case_data", srcs = [ - "expected.json", + "expected.yaml", ":fbs", ], visibility = ["//visibility:public"], diff --git a/validation/core/integration_test/sequence_internal_api/positive_self_call_method_match/expected.json b/validation/core/integration_test/sequence_internal_api/positive_self_call_method_match/expected.json deleted file mode 100644 index 208a55e1..00000000 --- a/validation/core/integration_test/sequence_internal_api/positive_self_call_method_match/expected.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "should_pass": true, - "error_contains": [] -} diff --git a/validation/core/integration_test/sequence_internal_api/positive_self_call_method_match/expected.yaml b/validation/core/integration_test/sequence_internal_api/positive_self_call_method_match/expected.yaml new file mode 100644 index 00000000..898ecba3 --- /dev/null +++ b/validation/core/integration_test/sequence_internal_api/positive_self_call_method_match/expected.yaml @@ -0,0 +1,13 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +should_pass: true diff --git a/validation/core/integration_test/sequence_internal_api/sequence_internal_api_suite.rs b/validation/core/integration_test/sequence_internal_api/sequence_internal_api_suite.rs index 097bc86f..0fec6d6d 100644 --- a/validation/core/integration_test/sequence_internal_api/sequence_internal_api_suite.rs +++ b/validation/core/integration_test/sequence_internal_api/sequence_internal_api_suite.rs @@ -12,7 +12,7 @@ // ******************************************************************************* use test_framework::{ - assert_cli_result, collect_case_fbs_files, load_expected_fixture, run_validation_profile, + assert_cli_result, collect_case_fbs_files, load_expected_yaml_fixture, run_validation_profile, CliRunResult, }; @@ -36,7 +36,7 @@ fn run_case_from_cli( } fn assert_case(case_dir: &str) { - let expected = load_expected_fixture(SUITE_DIR, case_dir); + let expected = load_expected_yaml_fixture(SUITE_DIR, case_dir); let component_fbs_paths = collect_case_fbs_files(SUITE_DIR, case_dir, "component"); let internal_api_fbs_paths = collect_case_fbs_files(SUITE_DIR, case_dir, "internal_api"); let sequence_fbs_paths = collect_case_fbs_files(SUITE_DIR, case_dir, "sequence"); diff --git a/validation/core/integration_test/src/lib.rs b/validation/core/integration_test/src/lib.rs index 3aacfe20..7312b824 100644 --- a/validation/core/integration_test/src/lib.rs +++ b/validation/core/integration_test/src/lib.rs @@ -14,7 +14,7 @@ mod test_framework; pub use test_framework::{ - assert_cli_result, case_file_path, collect_case_fbs_files, load_expected_fixture, - load_expected_yaml_fixture, normalize_yaml_result, run_validation_profile, CliRunResult, - ExpectedFixture, ValidationIntegrationCase, + assert_cli_result, case_file_path, collect_case_fbs_files, load_expected_yaml_fixture, + normalize_yaml_result, run_validation_profile, CliRunResult, ExpectedFixture, + ValidationIntegrationCase, }; diff --git a/validation/core/integration_test/src/test_framework.rs b/validation/core/integration_test/src/test_framework.rs index caf33316..ef26a206 100644 --- a/validation/core/integration_test/src/test_framework.rs +++ b/validation/core/integration_test/src/test_framework.rs @@ -114,14 +114,6 @@ pub fn collect_case_fbs_files(suite_dir: &str, case_dir: &str, category: &str) - matches } -pub fn load_expected_fixture(suite_dir: &str, case_dir: &str) -> ExpectedFixture { - let expected_json = read_case_file(&format!( - "validation/core/integration_test/{suite_dir}/{case_dir}/expected.json" - )); - - serde_json::from_str(&expected_json).expect("failed to parse expected fixture") -} - pub fn load_expected_yaml_fixture(suite_dir: &str, case_dir: &str) -> ExpectedFixture { let expected_yaml = read_case_file(&format!( "validation/core/integration_test/{suite_dir}/{case_dir}/expected.yaml" diff --git a/validation/core/src/lib.rs b/validation/core/src/lib.rs index e544dd4f..113882a7 100644 --- a/validation/core/src/lib.rs +++ b/validation/core/src/lib.rs @@ -23,4 +23,4 @@ mod results; mod validators; pub use profiles::{read_profile_inputs, run_profile, Profile, ProfileInputs, ProfileRun}; -pub use results::{Diagnostics, ValidationResult}; +pub use results::{Diagnostics, ErrorBuilder, ErrorCategory, ValidationResult}; diff --git a/validation/core/src/models/bazel_models.rs b/validation/core/src/models/bazel_models.rs index 61723cb9..0f4795f3 100644 --- a/validation/core/src/models/bazel_models.rs +++ b/validation/core/src/models/bazel_models.rs @@ -17,7 +17,7 @@ use serde::Deserialize; use super::shared::label_short_name; use super::EntityKey; -use crate::ValidationResult; +use crate::{ErrorBuilder, ErrorCategory, ValidationResult}; // --------------------------------------------------------------------------- /// Bazel architecture JSON model produced by the dependable element rule. @@ -62,11 +62,12 @@ impl BazelInput { // Top-level entries are dependable elements (SEooC). let key = (comp_key.clone(), None); if let Some(prev) = seooc_set.insert(key.clone(), comp_label.clone()) { - result.add_failure(format!( - "Duplicate dependable element key in Bazel build graph:\n\ - Key : {:?}\n\ - Labels: {} and {}", - key, prev, comp_label + result.add_failure(duplicate_bazel_entity_error( + "dependable element", + &comp_key, + None, + &prev, + comp_label, )); } } @@ -81,11 +82,12 @@ impl BazelInput { }; let key = (unit_key, Some(comp_key.clone())); if let Some(prev) = unit_set.insert(key.clone(), unit_label.clone()) { - result.add_failure(format!( - "Duplicate unit key in Bazel build graph:\n\ - Key : {:?}\n\ - Labels: {} and {}", - key, prev, unit_label + result.add_failure(duplicate_bazel_entity_error( + "unit", + &key.0, + Some(&comp_key), + &prev, + unit_label, )); } } @@ -100,11 +102,12 @@ impl BazelInput { }; let key = (component_key, Some(comp_key.clone())); if let Some(prev) = comp_set.insert(key.clone(), component_label.clone()) { - result.add_failure(format!( - "Duplicate component key in Bazel build graph:\n\ - Key : {:?}\n\ - Labels: {} and {}", - key, prev, component_label + result.add_failure(duplicate_bazel_entity_error( + "component", + &key.0, + Some(&comp_key), + &prev, + component_label, )); } } @@ -143,6 +146,39 @@ pub struct BazelArchitecture { pub unit_set: BTreeMap, } +fn duplicate_bazel_entity_error( + kind: &str, + alias: &str, + parent: Option<&str>, + first_label: &str, + second_label: &str, +) -> String { + let mut error = ErrorBuilder::new(ErrorCategory::Design) + .title(format!( + "{kind} \"{alias}\" is defined more than once in Bazel." + )) + .field("alias", format!("\"{alias}\"")); + + if let Some(parent) = parent { + error = error.field("parent", parent); + } + + let fix = match parent { + Some(parent) => format!( + "keep only one Bazel {kind} definition for \"{alias}\" under \"{parent}\", or rename one of the duplicate Bazel targets" + ), + None => format!( + "keep only one Bazel {kind} definition for \"{alias}\", or rename one of the duplicate Bazel targets" + ), + }; + + error + .field("first bazel label", first_label) + .field("second bazel label", second_label) + .fix(fix) + .build() +} + #[cfg(test)] mod tests { use super::*; @@ -172,11 +208,9 @@ mod tests { let _architecture = arch.to_bazel_architecture(&mut setup_result); assert!( - setup_result - .failures - .iter() - .any(|message| message.contains("Duplicate dependable element key")), - "Expected duplicate dependable element key error, got: {:?}", + setup_result.failures.iter().any(|message| message + .contains("Dependable element \"comp_a\" is defined more than once in Bazel.")), + "Expected duplicate dependable element design error, got: {:?}", setup_result.failures ); } diff --git a/validation/core/src/models/class_diagram_models.rs b/validation/core/src/models/class_diagram_models.rs index ef3a6c54..0fd78288 100644 --- a/validation/core/src/models/class_diagram_models.rs +++ b/validation/core/src/models/class_diagram_models.rs @@ -16,8 +16,9 @@ use std::collections::{BTreeMap, BTreeSet}; use class_diagram::{ClassDiagram as ClassDiagramInput, EntityType, SimpleEntity}; +use source_location::SourceLocation; -use crate::ValidationResult; +use crate::{ErrorBuilder, ErrorCategory, ValidationResult}; /// Collection of class diagrams loaded from one or more FlatBuffer files. pub type ClassDiagramInputs = Vec; @@ -38,13 +39,38 @@ impl ClassEntityIndex { let key = indexed_entity.id.to_lowercase(); if let Some(prev) = entities.get(&key) { - result.add_failure(format!( - "Duplicate class entity in validation input:\n\ - Key : {key}\n\ - First location : {}\n\ - Second location : {}", - prev.source_location, indexed_entity.source_location - )); + let (first_source_file, _) = prev.source_location.display(); + let (second_source_file, _) = indexed_entity.source_location.display(); + + result.add_failure( + ErrorBuilder::new(ErrorCategory::Class) + .title(format!( + "class \"{}\" is defined more than once in the class diagram.", + prev.id, + )) + .field("class", format!("\"{}\"", prev.id)) + .field( + "design source file", + format!("\"{}\"", first_source_file), + ) + .field( + "design source line", + prev.source_location.line.to_string(), + ) + .field( + "duplicate source file", + format!("\"{}\"", second_source_file), + ) + .field( + "duplicate source line", + indexed_entity.source_location.line.to_string(), + ) + .fix(format!( + "remove or rename one of the duplicate class \"{}\" declarations in the class diagram", + prev.id, + )) + .build(), + ); } else { entities.insert(key, indexed_entity); } @@ -67,6 +93,7 @@ impl ClassEntityIndex { pub struct InternalApiInterface { pub id: String, pub method_names: BTreeSet, + pub source_location: SourceLocation, } /// Indexed internal-API data prepared for validators. @@ -93,6 +120,7 @@ impl InternalApiIndex { .map(|method| method.name.clone()) .filter(|name| !name.is_empty()) .collect(), + source_location: entity.source_location.clone(), }; interfaces.push(interface); @@ -138,7 +166,8 @@ impl PublicApiIndex { #[cfg(test)] mod tests { use super::*; - use class_diagram::{ClassDiagram, Method, SimpleEntity, SourceLocation, Visibility}; + use class_diagram::{ClassDiagram, Method, SimpleEntity, Visibility}; + use source_location::SourceLocation; fn method(name: &str) -> Method { Method { @@ -182,27 +211,9 @@ mod tests { let _index = ClassEntityIndex::build_index(&diagrams, &mut result); assert_eq!(result.failures.len(), 1); - assert!(result.failures[0].contains("Key : unit.sample")); - assert!(result.failures[0].contains("First location : design_a.puml:12")); - assert!(result.failures[0].contains("Second location : design_b.puml:34")); - } - - #[test] - fn class_entity_index_reports_duplicate_source_locations_with_distinct_files() { - let diagrams = vec![ClassDiagram { - name: "classes".to_string(), - entities: vec![ - entity("Unit.Sample", "design_left.puml", 1), - entity("unit.sample", "design_right.puml", 2), - ], - }]; - - let mut result = ValidationResult::default(); - let _index = ClassEntityIndex::build_index(&diagrams, &mut result); - - assert_eq!(result.failures.len(), 1); - assert!(result.failures[0].contains("First location : design_left.puml:1")); - assert!(result.failures[0].contains("Second location : design_right.puml:2")); + assert!(result.failures[0].contains( + "[Class] Class \"Unit.Sample\" is defined more than once in the class diagram." + )); } #[test] diff --git a/validation/core/src/models/component_diagram_models.rs b/validation/core/src/models/component_diagram_models.rs index 0881273d..2e06aacd 100644 --- a/validation/core/src/models/component_diagram_models.rs +++ b/validation/core/src/models/component_diagram_models.rs @@ -14,7 +14,7 @@ use std::collections::BTreeMap; use super::EntityKey; -use crate::ValidationResult; +use crate::{ErrorBuilder, ErrorCategory, ValidationResult}; pub use component_diagram::{ ComponentRelationType, ComponentType, EndpointRole, LogicComponent, LogicRelation, }; @@ -104,12 +104,28 @@ impl ComponentDiagramArchitecture { for entity in entities { let key = entity.id.to_lowercase(); if let Some(prev) = id_index.insert(key.clone(), entity) { - result.add_failure(format!( - "Duplicate entity ID in PlantUML diagram (case-insensitive):\n\ - ID : {key:?}\n\ - IDs: {} and {}", - prev.id, entity.id - )); + let kind = entity_kind_name(entity); + let alias = entity.match_key(); + let parent = + entity_parent_alias(entity, &id_index).unwrap_or_else(|| "".to_string()); + let ((source_file, source_line), (duplicate_file, duplicate_line)) = + ordered_source_locations(&prev.source_location, &entity.source_location); + result.add_failure( + ErrorBuilder::new(ErrorCategory::Design) + .title(format!( + "{kind} \"{alias}\" is defined more than once in the component diagram." + )) + .field(kind, format!("\"{alias}\"")) + .field("parent", &parent) + .field("component source file", format!("\"{source_file}\"")) + .field("component source line", source_line.to_string()) + .field("duplicate source file", format!("\"{duplicate_file}\"")) + .field("duplicate source line", duplicate_line.to_string()) + .fix(format!( + "keep only one {kind} \"{alias}\" under \"{parent}\", or rename one of the duplicate entities" + )) + .build(), + ); } } @@ -155,13 +171,23 @@ impl ComponentDiagramArchitecture { Some(parent_id) => match id_index.get(&parent_id.to_lowercase()) { Some(parent) => Some(parent.match_key()), None => { - result.add_failure(format!( - "Unresolved parent_id in PlantUML diagram:\n\ - Entity ID : {}\n\ - Parent ID : {}\n\ - Action : Fix the parent reference or add the missing parent entity", - entity.id, parent_id - )); + let kind = entity_kind_name(entity); + let alias = entity.match_key(); + let (source_file, source_line) = entity.source_location.display(); + result.add_failure( + ErrorBuilder::new(ErrorCategory::Design) + .title(format!( + "{kind} \"{alias}\" references a parent that is not defined in the component diagram." + )) + .field(kind, format!("\"{alias}\"")) + .field("parent", format!("\"{parent_id}\"")) + .field("component source file", format!("\"{source_file}\"")) + .field("component source line", source_line.to_string()) + .fix(format!( + "update the parent reference for {kind} \"{alias}\", or add the missing parent entity in the component diagram" + )) + .build(), + ); None } }, @@ -169,18 +195,78 @@ impl ComponentDiagramArchitecture { }; let key = (alias, parent_alias); if let Some(prev) = set.insert(key.clone(), (*entity).clone()) { - result.add_failure(format!( - "Duplicate entity in PlantUML diagram:\n\ - Key: {:?}\n\ - IDs: {} and {}", - key, prev.id, entity.id - )); + if prev.id.eq_ignore_ascii_case(&entity.id) { + continue; + } + let kind = entity_kind_name(entity); + let alias = entity.match_key(); + let parent = key.1.as_deref().unwrap_or(""); + let ((source_file, source_line), (duplicate_file, duplicate_line)) = + ordered_source_locations(&prev.source_location, &entity.source_location); + result.add_failure( + ErrorBuilder::new(ErrorCategory::Design) + .title(format!( + "{kind} \"{alias}\" is defined more than once in the component diagram." + )) + .field(kind, format!("\"{alias}\"")) + .field("parent", parent) + .field("component source file", format!("\"{source_file}\"")) + .field("component source line", source_line.to_string()) + .field("duplicate source file", format!("\"{duplicate_file}\"")) + .field("duplicate source line", duplicate_line.to_string()) + .fix( + format!( + "keep only one {kind} \"{alias}\" under \"{parent}\", or rename one of the duplicate entities" + ), + ) + .build(), + ); } } set } } +fn entity_kind_name(entity: &LogicComponent) -> &'static str { + if entity.is_unit() { + "unit" + } else if entity.is_component() { + "component" + } else if entity.is_interface() { + "interface" + } else if entity.is_seooc_package() { + "dependable element" + } else { + "entity" + } +} + +fn entity_parent_alias( + entity: &LogicComponent, + id_index: &BTreeMap, +) -> Option { + entity.parent_id.as_deref().map(|parent_id| { + id_index + .get(&parent_id.to_lowercase()) + .map(|parent| parent.match_key()) + .unwrap_or_else(|| parent_id.to_string()) + }) +} + +fn ordered_source_locations( + left: &source_location::SourceLocation, + right: &source_location::SourceLocation, +) -> ((String, u32), (String, u32)) { + let left_display = left.display(); + let right_display = right.display(); + + if (left_display.0.as_str(), left_display.1) <= (right_display.0.as_str(), right_display.1) { + (left_display, right_display) + } else { + (right_display, left_display) + } +} + #[cfg(test)] mod tests { use super::*; @@ -309,8 +395,9 @@ mod tests { set_result .failures .iter() - .any(|message| message.contains("Duplicate entity ID")), - "Expected duplicate ID error, got: {:?}", + .any(|message| message + .contains("is defined more than once in the component diagram")), + "Expected duplicate component-diagram entity error, got: {:?}", set_result.failures ); } @@ -345,7 +432,9 @@ mod tests { set_result .failures .iter() - .any(|message| message.contains("Unresolved parent_id")), + .any(|message| message.contains( + "Component \"comp_a\" references a parent that is not defined in the component diagram." + )), "Expected unresolved parent error, got: {:?}", set_result.failures ); diff --git a/validation/core/src/models/sequence_diagram_models.rs b/validation/core/src/models/sequence_diagram_models.rs index d4478ed3..93b4ced0 100644 --- a/validation/core/src/models/sequence_diagram_models.rs +++ b/validation/core/src/models/sequence_diagram_models.rs @@ -13,11 +13,12 @@ //! Models for sequence-diagram FlatBuffer inputs used by design verification. -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; -use sequence_logic::{Event, SequenceNode, SequenceTree}; +use sequence_logic::{Event, SequenceNode, SequenceParticipant, SequenceTree}; +use source_location::SourceLocation; -use crate::ValidationResult; +use crate::{ErrorBuilder, ErrorCategory, ValidationResult}; /// Collection of sequence diagrams loaded from one or more FlatBuffer files. pub struct SequenceDiagramInputs { @@ -29,8 +30,7 @@ pub struct ObservedSequenceCall { pub caller: String, pub callee: String, pub method: String, - pub source_file: String, - pub source_line: u32, + pub source_location: SourceLocation, } impl SequenceDiagramInputs { @@ -44,22 +44,32 @@ impl SequenceDiagramInputs { pub struct SequenceDiagramIndex { used_participants: BTreeSet, observed_calls: Vec, + participant_sources: BTreeMap, } impl SequenceDiagramIndex { fn from_diagrams(diagrams: &[SequenceTree], result: &mut ValidationResult) -> Self { let mut used_participants = BTreeSet::new(); let mut observed_calls = Vec::new(); + let mut participant_sources = BTreeMap::new(); for diagram in diagrams { + collect_participant_sources(&diagram.participants, &mut participant_sources); for node in &diagram.root_interactions { - collect_sequence_data(node, &mut used_participants, &mut observed_calls, result); + collect_sequence_data( + node, + &mut used_participants, + &mut observed_calls, + &mut participant_sources, + result, + ); } } Self { used_participants, observed_calls, + participant_sources, } } @@ -70,95 +80,149 @@ impl SequenceDiagramIndex { pub fn observed_calls(&self) -> &[ObservedSequenceCall] { &self.observed_calls } + + pub fn participant_source(&self, participant: &str) -> Option<&SourceLocation> { + self.participant_sources.get(participant) + } +} + +fn collect_participant_sources( + participants: &[SequenceParticipant], + participant_sources: &mut BTreeMap, +) { + for participant in participants { + participant_sources + .entry(participant_name(participant)) + .or_insert_with(|| participant.source_location.clone()); + } } fn collect_sequence_data( node: &SequenceNode, used_participants: &mut BTreeSet, observed_calls: &mut Vec, + participant_sources: &mut BTreeMap, result: &mut ValidationResult, ) { match &node.event { Event::Interaction(interaction) => { + let (source_file, source_line) = node.source_location.display(); validate_required_endpoints( result, RequiredEndpointsCheck { - connection_kind: "sequence function-call connection", + item_kind: "sequence function", caller: interaction.caller.as_str(), callee: interaction.callee.as_str(), label_value: interaction.method.as_str(), - label_name: "Sequence function", - action: - "Provide both caller and callee for each sequence function-call connection", - source_file: node.source_location.file.as_ref(), - source_line: node.source_location.line, + label_name: "method", + source_file: source_file.as_str(), + source_line, }, ); - if !interaction.caller.is_empty() { - used_participants.insert(interaction.caller.clone()); - } - if !interaction.callee.is_empty() { - used_participants.insert(interaction.callee.clone()); - } + record_participant_usage_and_source( + interaction.caller.as_str(), + &node.source_location, + used_participants, + participant_sources, + ); + record_participant_usage_and_source( + interaction.callee.as_str(), + &node.source_location, + used_participants, + participant_sources, + ); observed_calls.push(ObservedSequenceCall { caller: interaction.caller.clone(), callee: interaction.callee.clone(), method: interaction.method.clone(), - source_file: node.source_location.file.to_string(), - source_line: node.source_location.line, + source_location: node.source_location.clone(), }); } Event::Return(ret) => { + let (source_file, source_line) = node.source_location.display(); validate_required_endpoints( result, RequiredEndpointsCheck { - connection_kind: "sequence return connection", + item_kind: "sequence return", caller: ret.caller.as_str(), callee: ret.callee.as_str(), label_value: ret.return_content.as_str(), - label_name: "Return content", - action: "Provide both caller and callee for each sequence return connection", - source_file: node.source_location.file.as_ref(), - source_line: node.source_location.line, + label_name: "return content", + source_file: source_file.as_str(), + source_line, }, ); - if !ret.caller.is_empty() { - used_participants.insert(ret.caller.clone()); - } - if !ret.callee.is_empty() { - used_participants.insert(ret.callee.clone()); - } + record_participant_usage_and_source( + ret.caller.as_str(), + &node.source_location, + used_participants, + participant_sources, + ); + record_participant_usage_and_source( + ret.callee.as_str(), + &node.source_location, + used_participants, + participant_sources, + ); } Event::Condition(_) => {} } for child in &node.branches_node { - collect_sequence_data(child, used_participants, observed_calls, result); + collect_sequence_data( + child, + used_participants, + observed_calls, + participant_sources, + result, + ); } } +fn record_participant_usage_and_source( + participant: &str, + source_location: &SourceLocation, + used_participants: &mut BTreeSet, + participant_sources: &mut BTreeMap, +) { + if participant.is_empty() { + return; + } + + used_participants.insert(participant.to_string()); + participant_sources + .entry(participant.to_string()) + .or_insert_with(|| source_location.clone()); +} + +fn participant_name(participant: &SequenceParticipant) -> String { + participant + .alias + .clone() + .filter(|alias| !alias.is_empty()) + .unwrap_or_else(|| participant.display_name.clone()) +} + struct RequiredEndpointsCheck<'a> { - connection_kind: &'a str, + item_kind: &'a str, caller: &'a str, callee: &'a str, label_value: &'a str, label_name: &'a str, - action: &'a str, source_file: &'a str, source_line: u32, } fn validate_required_endpoints(result: &mut ValidationResult, check: RequiredEndpointsCheck<'_>) { let RequiredEndpointsCheck { - connection_kind, + item_kind, caller, callee, label_value, label_name, - action, source_file, source_line, } = check; @@ -174,18 +238,23 @@ fn validate_required_endpoints(result: &mut ValidationResult, check: RequiredEnd (false, false) => unreachable!(), }; - result.add_failure(format!( - "Sequence validity failure: {connection_kind} is missing required endpoints:\n\ - Missing endpoints : \"{missing_endpoints}\"\n\ - Caller unit : \"{caller}\"\n\ - Callee unit : \"{callee}\"\n\ - {label_name:<18}: \"{label_value}\"\n\ - Source file : \"{source_file}\"\n\ - Source line : \"{source_line}\"\n\ - Action : {action}", - source_file = source_file, - source_line = source_line, - )); + let fix = format!( + "add the missing {missing_endpoints} for {item_kind} \"{label_value}\" in the sequence diagram" + ); + + result.add_failure( + ErrorBuilder::new(ErrorCategory::Method) + .title(format!( + "{item_kind} \"{label_value}\" is missing {missing_endpoints}." + )) + .field(label_name, format!("\"{label_value}\"")) + .field("caller unit", format!("\"{caller}\"")) + .field("callee unit", format!("\"{callee}\"")) + .field("sequence source file", format!("\"{source_file}\"")) + .field("sequence source line", source_line.to_string()) + .fix(fix) + .build(), + ); } #[cfg(test)] @@ -277,8 +346,7 @@ mod tests { assert_eq!(result.failures.len(), 1); assert!(result.failures[0] - .contains("sequence function-call connection is missing required endpoints")); - assert!(result.failures[0].contains("\"caller\"")); + .contains("[Method] Sequence function \"GetData()\" is missing caller.")); assert!(result.failures[0].contains("\"unit_2\"")); } @@ -297,8 +365,7 @@ mod tests { assert_eq!(result.failures.len(), 1); assert!(result.failures[0] - .contains("sequence function-call connection is missing required endpoints")); - assert!(result.failures[0].contains("\"callee\"")); + .contains("[Method] Sequence function \"GetData()\" is missing callee.")); assert!(result.failures[0].contains("\"unit_1\"")); } } diff --git a/validation/core/src/models/shared.rs b/validation/core/src/models/shared.rs index 63704261..412111a2 100644 --- a/validation/core/src/models/shared.rs +++ b/validation/core/src/models/shared.rs @@ -13,6 +13,8 @@ //! Shared helper types used across the split validation models. +use crate::{ErrorBuilder, ErrorCategory}; + /// Composite key: `(canonical_alias, parent_alias)`. `parent_alias` is `None` /// for top-level entities. Using the parent as part of the key means two /// identically-named entities under different parents are treated as distinct. @@ -24,7 +26,13 @@ pub type EntityKey = (String, Option); pub(super) fn label_short_name(label: &str) -> Result<&str, String> { let name = label.rsplit_once(':').map(|(_, n)| n).unwrap_or(label); if name.is_empty() { - return Err(format!("Empty target name extracted from label: {label:?}")); + return Err(ErrorBuilder::new(ErrorCategory::Design) + .title(format!("Bazel label \"{label}\" does not define a target.")) + .field("bazel label", format!("\"{label}\"")) + .fix(format!( + "add a target name after ':' in Bazel label \"{label}\"" + )) + .build()); } Ok(name) } diff --git a/validation/core/src/readers/mod.rs b/validation/core/src/readers/mod.rs index 3332d209..68793daa 100644 --- a/validation/core/src/readers/mod.rs +++ b/validation/core/src/readers/mod.rs @@ -13,7 +13,7 @@ //! Input readers for Bazel JSON and PlantUML-derived FlatBuffer artifacts. -use component_diagram::SourceLocation; +use source_location::SourceLocation; mod bazel_reader; mod class_diagram_reader; diff --git a/validation/core/src/results/error_models.rs b/validation/core/src/results/error_models.rs new file mode 100644 index 00000000..951e64d0 --- /dev/null +++ b/validation/core/src/results/error_models.rs @@ -0,0 +1,125 @@ +// ******************************************************************************* +// 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 +// +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* + +//! Shared error message formatting helpers for validation results. + +use std::fmt::Write; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ErrorCategory { + Design, + Class, + Member, + Implementation, + Naming, + Interface, + Method, + Coverage, +} + +impl ErrorCategory { + fn as_tag(self) -> &'static str { + match self { + Self::Design => "Design", + Self::Class => "Class", + Self::Member => "Member", + Self::Implementation => "Implementation", + Self::Naming => "Naming", + Self::Interface => "Interface", + Self::Method => "Method", + Self::Coverage => "Coverage", + } + } +} + +fn uppercase_first_char(value: impl Into) -> String { + let mut value = value.into(); + + if let Some((index, first_char)) = value.char_indices().next() { + let upper = first_char.to_uppercase().to_string(); + value.replace_range(index..index + first_char.len_utf8(), &upper); + } + + value +} + +fn normalize_sentence_case(value: impl Into) -> String { + let mut value = uppercase_first_char(value); + + if !value.ends_with('.') { + value.push('.'); + } + + value +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ErrorBuilder { + category: ErrorCategory, + title: Option, + fields: Vec<(String, String)>, + fix: Option, +} + +impl ErrorBuilder { + pub fn new(category: ErrorCategory) -> Self { + Self { + category, + title: None, + fields: Vec::new(), + fix: None, + } + } + + pub fn title(mut self, title: impl Into) -> Self { + self.title = Some(normalize_sentence_case(title)); + self + } + + pub fn field(mut self, key: impl Into, value: impl Into) -> Self { + self.fields.push((uppercase_first_char(key), value.into())); + self + } + + pub fn fix(mut self, fix: impl Into) -> Self { + self.fix = Some(normalize_sentence_case(fix)); + self + } + + pub fn build(self) -> String { + let label_width = self + .fields + .iter() + .map(|(key, _)| key.len()) + .chain(self.fix.iter().map(|_| "Fix".len())) + .max() + .unwrap_or(0); + + let title = self.title.unwrap_or_default(); + let mut message = String::new(); + write!(&mut message, "[{}] {title}", self.category.as_tag()) + .expect("writing to String cannot fail"); + + for (key, value) in self.fields { + write!(&mut message, "\n {key:label_width$} : {value}") + .expect("writing to String cannot fail"); + } + + if let Some(fix) = self.fix { + write!(&mut message, "\n {:label_width$} : {fix}", "Fix") + .expect("writing to String cannot fail"); + } + + message + } +} diff --git a/validation/core/src/results/mod.rs b/validation/core/src/results/mod.rs index 15514e9f..8cf30cde 100644 --- a/validation/core/src/results/mod.rs +++ b/validation/core/src/results/mod.rs @@ -14,8 +14,10 @@ //! Validation execution results. mod diagnostics; +mod error_models; pub use diagnostics::Diagnostics; +pub use error_models::{ErrorBuilder, ErrorCategory}; #[derive(Debug, Default)] pub struct ValidationResult { diff --git a/validation/core/src/validators/bazel_component_validator.rs b/validation/core/src/validators/bazel_component_validator.rs index 07951b01..97cfbcc2 100644 --- a/validation/core/src/validators/bazel_component_validator.rs +++ b/validation/core/src/validators/bazel_component_validator.rs @@ -17,7 +17,10 @@ //! [`BazelComponentValidator`] performs a two-way set-difference between a //! [`BazelArchitecture`] and a [`ComponentDiagramArchitecture`]. -use crate::models::{BazelArchitecture, ComponentDiagramArchitecture}; +use std::collections::BTreeMap; + +use crate::models::{BazelArchitecture, ComponentDiagramArchitecture, LogicComponent}; +use crate::results::{ErrorBuilder, ErrorCategory}; use crate::{Diagnostics, ValidationResult}; /// Run bazel-vs-component architecture validation using indexed inputs. @@ -52,52 +55,46 @@ impl BazelComponentValidator { diagram: &ComponentDiagramArchitecture, ) -> ValidationResult { append_debug_log(&mut self.result.diagnostics, bazel, diagram); - self.check_seooc(bazel, diagram); - self.check_components(bazel, diagram); - self.check_units(bazel, diagram); + self.check_entity_set( + &bazel.seooc_set, + &diagram.seooc_set, + "package", + "SEooC", + "(top-level)", + ); + self.check_entity_set( + &bazel.comp_set, + &diagram.comp_set, + "component", + "component", + "(top-level)", + ); + self.check_entity_set( + &bazel.unit_set, + &diagram.unit_set, + "unit", + "unit", + "(no parent?)", + ); self.result } - fn check_seooc(&mut self, bazel: &BazelArchitecture, diagram: &ComponentDiagramArchitecture) { - // In Bazel but not in PlantUML -> MISSING. - for (key, label) in &bazel.seooc_set { - if !diagram.seooc_set.contains_key(key) { - let (name, _) = key; - self.result.add_failure(Self::format_missing( - "package", - "SEooC", - name, - "(top-level)", - label, - )); - } - } - - // In PlantUML but not in Bazel -> EXTRA. - for key in diagram.seooc_set.keys() { - if !bazel.seooc_set.contains_key(key) { - let (name, _) = key; - self.result - .add_failure(Self::format_extra("package", name, "(top-level)")); - } - } - } - - fn check_components( + fn check_entity_set( &mut self, - bazel: &BazelArchitecture, - diagram: &ComponentDiagramArchitecture, + bazel_set: &BTreeMap<(String, Option), String>, + diagram_set: &BTreeMap<(String, Option), LogicComponent>, + display_type: &str, + stereotype: &str, + default_parent: &str, ) { // In Bazel but not in PlantUML -> MISSING. - for (key, label) in &bazel.comp_set { - if !diagram.comp_set.contains_key(key) { + for (key, label) in bazel_set { + if !diagram_set.contains_key(key) { let (name, parent) = key; - let parent_str = parent - .as_ref() - .map_or("(top-level)".to_string(), |value| value.clone()); + let parent_str = Self::parent_display(parent, default_parent); self.result.add_failure(Self::format_missing( - "component", - "component", + display_type, + stereotype, name, &parent_str, label, @@ -106,47 +103,24 @@ impl BazelComponentValidator { } // In PlantUML but not in Bazel -> EXTRA. - for key in diagram.comp_set.keys() { - if !bazel.comp_set.contains_key(key) { + for (key, entity) in diagram_set { + if !bazel_set.contains_key(key) { let (name, parent) = key; - let parent_str = parent - .as_ref() - .map_or("(top-level)".to_string(), |value| value.clone()); - self.result - .add_failure(Self::format_extra("component", name, &parent_str)); - } - } - } - - fn check_units(&mut self, bazel: &BazelArchitecture, diagram: &ComponentDiagramArchitecture) { - // In Bazel but not in PlantUML -> MISSING. - for (key, label) in &bazel.unit_set { - if !diagram.unit_set.contains_key(key) { - let (name, parent) = key; - let parent_str = parent - .as_ref() - .map_or("(no parent?)".to_string(), |value| value.clone()); - self.result.add_failure(Self::format_missing( - "unit", - "unit", + let parent_str = Self::parent_display(parent, default_parent); + self.result.add_failure(Self::format_extra( + display_type, name, &parent_str, - label, + entity, )); } } + } - // In PlantUML but not in Bazel -> EXTRA. - for key in diagram.unit_set.keys() { - if !bazel.unit_set.contains_key(key) { - let (name, parent) = key; - let parent_str = parent - .as_ref() - .map_or("(no parent?)".to_string(), |value| value.clone()); - self.result - .add_failure(Self::format_extra("unit", name, &parent_str)); - } - } + fn parent_display(parent: &Option, default_parent: &str) -> String { + parent + .as_ref() + .map_or(default_parent.to_string(), |value| value.clone()) } fn format_missing( @@ -156,22 +130,40 @@ impl BazelComponentValidator { parent_str: &str, label: &str, ) -> String { - format!( - "Missing {display_type} in PlantUML:\n\ - Alias : \"{name}\"\n\ - Parent : {parent_str}\n\ - Bazel label : {label}\n\ - Required : Add {display_type} with alias \"{name}\" and stereotype <<{stereotype}>>", - ) + ErrorBuilder::new(ErrorCategory::Naming) + .title(format!( + "{display_type} \"{name}\" from Bazel not found in the PlantUML component diagram" + )) + .field("alias", format!("\"{name}\"")) + .field("parent", parent_str) + .field("stereotype", format!("<<{stereotype}>>")) + .field("bazel label", label) + .fix(format!( + "add {display_type} \"{name}\" with stereotype <<{stereotype}>> in the PlantUML component diagram, or remove it from Bazel" + )) + .build() } - fn format_extra(entity_type: &str, name: &str, parent_str: &str) -> String { - format!( - "Extra {entity_type} in PlantUML not in Bazel:\n\ - Alias : \"{name}\"\n\ - Parent : {parent_str}\n\ - Action : Remove this {entity_type} or add to Bazel", - ) + fn format_extra( + entity_type: &str, + name: &str, + parent_str: &str, + entity: &LogicComponent, + ) -> String { + let (source_file, source_line) = entity.source_location.display(); + + ErrorBuilder::new(ErrorCategory::Naming) + .title(format!( + "{entity_type} \"{name}\" from the PlantUML component diagram not found in Bazel" + )) + .field("alias", format!("\"{name}\"")) + .field("parent", parent_str) + .field("component source file", format!("\"{source_file}\"")) + .field("component source line", source_line.to_string()) + .fix(format!( + "add the corresponding Bazel {entity_type} definition for \"{name}\", or remove it from the PlantUML component diagram" + )) + .build() } } @@ -384,7 +376,9 @@ mod tests { ]); let errs = run_arch_validation(&arch, &diagram); assert!(!errs.is_empty()); - assert!(errs.failures.iter().any(|m| m.contains("Missing unit"))); + assert!(errs.failures.iter().any(|m| { + m.contains("Unit \"unit_2\" from Bazel not found in the PlantUML component diagram.") + })); } #[test] @@ -425,7 +419,9 @@ mod tests { let errs = run_arch_validation(&arch, &diagram); assert!(!errs.is_empty()); assert!( - errs.failures.iter().any(|m| m.contains("Extra component")), + errs.failures.iter().any(|m| m.contains( + "Component \"extra_comp\" from the PlantUML component diagram not found in Bazel." + )), "Expected extra component error, got: {:?}", errs.failures ); @@ -445,7 +441,9 @@ mod tests { let errs = run_arch_validation(&arch, &diagram); assert!(!errs.is_empty()); assert!( - errs.failures.iter().any(|m| m.contains("Extra unit")), + errs.failures.iter().any(|m| m.contains( + "Unit \"extra_unit\" from the PlantUML component diagram not found in Bazel." + )), "Expected extra unit error, got: {:?}", errs.failures ); @@ -461,7 +459,9 @@ mod tests { "<> should not satisfy <> requirement" ); assert!( - errs.failures.iter().any(|m| m.contains("Missing package")), + errs.failures.iter().any(|m| m.contains( + "Package \"my_de\" from Bazel not found in the PlantUML component diagram." + )), "Expected missing package error, got: {:?}", errs.failures ); @@ -483,9 +483,9 @@ mod tests { "<> should not satisfy <> requirement" ); assert!( - errs.failures - .iter() - .any(|m| m.contains("Missing component")), + errs.failures.iter().any(|m| m.contains( + "Component \"comp_a\" from Bazel not found in the PlantUML component diagram." + )), "Expected missing component error, got: {:?}", errs.failures ); diff --git a/validation/core/src/validators/class_design_implementation_validator.rs b/validation/core/src/validators/class_design_implementation_validator.rs index 55bc74f3..db3ac953 100644 --- a/validation/core/src/validators/class_design_implementation_validator.rs +++ b/validation/core/src/validators/class_design_implementation_validator.rs @@ -12,9 +12,10 @@ // ******************************************************************************* //! Class implementation validation: compare unit design class diagrams with -//! implementation class diagrams produced by the C++ parser. +//! C++ implementation produced by the C++ parser. use crate::models::ClassEntityIndex; +use crate::results::{ErrorBuilder, ErrorCategory}; use crate::ValidationResult; use class_diagram::{ EnumLiteral, MemberVariable, Method, Relationship, SimpleEntity, TemplateParameter, TypeAlias, @@ -33,6 +34,12 @@ struct ClassDesignImplementationValidator { result: ValidationResult, } +#[derive(Clone, Copy)] +enum FieldReferenceStyle { + Quoted, + Verbatim, +} + impl ClassDesignImplementationValidator { fn new() -> Self { Self { @@ -92,6 +99,7 @@ impl ClassDesignImplementationValidator { design_entity, implementation_entity, "entity_type", + FieldReferenceStyle::Quoted, &format!("{:?}", design_entity.entity_type), &format!("{:?}", implementation_entity.entity_type), )); @@ -102,6 +110,7 @@ impl ClassDesignImplementationValidator { design_entity, implementation_entity, "template_parameters", + FieldReferenceStyle::Quoted, &render_template_parameters(&design_entity.template_parameters), &render_template_parameters(&implementation_entity.template_parameters), )); @@ -127,7 +136,11 @@ impl ClassDesignImplementationValidator { Some(implementation_alias) => self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("type_alias {:?} original_type", design_alias.alias.as_str()), + &format!( + "type alias \"{}\" original type", + design_alias.alias.as_str() + ), + FieldReferenceStyle::Verbatim, &design_alias.original_type, &implementation_alias.original_type, )), @@ -180,7 +193,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("variable {variable_name:?} data_type"), + &format!("variable \"{variable_name}\" data type"), + FieldReferenceStyle::Verbatim, &render_optional_type(&design_variable.data_type), &render_optional_type(&implementation_variable.data_type), )); @@ -190,7 +204,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("variable {variable_name:?} visibility"), + &format!("variable \"{variable_name}\" visibility"), + FieldReferenceStyle::Verbatim, &format!("{:?}", design_variable.visibility), &format!("{:?}", implementation_variable.visibility), )); @@ -200,7 +215,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("variable {variable_name:?} is_static"), + &format!("variable \"{variable_name}\" is static"), + FieldReferenceStyle::Verbatim, &format!("{:?}", design_variable.is_static), &format!("{:?}", implementation_variable.is_static), )); @@ -263,7 +279,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("method {method_name:?} return_type"), + &format!("method \"{method_name}\" return type"), + FieldReferenceStyle::Verbatim, &render_optional_type(&design_method.return_type), &render_optional_type(&implementation_method.return_type), )); @@ -273,7 +290,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("method {method_name:?} visibility"), + &format!("method \"{method_name}\" visibility"), + FieldReferenceStyle::Verbatim, &format!("{:?}", design_method.visibility), &format!("{:?}", implementation_method.visibility), )); @@ -290,7 +308,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("method {method_name:?} template_parameters"), + &format!("method \"{method_name}\" template parameters"), + FieldReferenceStyle::Verbatim, &render_template_parameters(&design_method.template_parameters), &render_template_parameters(&implementation_method.template_parameters), )); @@ -300,7 +319,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("method {method_name:?} modifiers"), + &format!("method \"{method_name}\" modifiers"), + FieldReferenceStyle::Verbatim, &format!("{:?}", design_method.modifiers), &format!("{:?}", implementation_method.modifiers), )); @@ -320,7 +340,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("method {method_name:?} parameter_count"), + &format!("method \"{method_name}\" parameter count"), + FieldReferenceStyle::Verbatim, &format_parameter_count(&design_method.parameters), &format_parameter_count(&implementation_method.parameters), )); @@ -336,7 +357,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("method {method_name:?} parameter name"), + &format!("method \"{method_name}\" parameter name"), + FieldReferenceStyle::Verbatim, &format_parameter(design_parameter), &format_parameter(implementation_parameter), )); @@ -348,7 +370,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("method {method_name:?} parameter type"), + &format!("method \"{method_name}\" parameter type"), + FieldReferenceStyle::Verbatim, &format_parameter(design_parameter), &format_parameter(implementation_parameter), )); @@ -358,7 +381,8 @@ impl ClassDesignImplementationValidator { self.result.add_failure(Self::format_mismatch( design_entity, implementation_entity, - &format!("method {method_name:?} parameter list"), + &format!("method \"{method_name}\" parameter list"), + FieldReferenceStyle::Verbatim, &format_method_parameters(&design_method.parameters), &format_method_parameters(&implementation_method.parameters), )); @@ -380,6 +404,7 @@ impl ClassDesignImplementationValidator { design_entity, implementation_entity, &format!("{:?}", design_literal.name.as_str()), + FieldReferenceStyle::Verbatim, &enum_literal_value(design_literal), &enum_literal_value(implementation_literal), )), @@ -410,6 +435,7 @@ impl ClassDesignImplementationValidator { design_entity, implementation_entity, "relationship type", + FieldReferenceStyle::Quoted, &display_name, &relationship_display_name(implementation_relationship), )) @@ -424,15 +450,19 @@ impl ClassDesignImplementationValidator { } fn format_missing_class(entity: &SimpleEntity) -> String { - let (design_file, design_line) = entity.source_location.display(); - format!( - "Missing implementation class for unit design entity:\n\ - Entity ID : {}\n\ - Design source file : {}\n\ - Design source line : {}\n\ - Required Action : Add a matching implementation class or update the unit design", - entity.id, design_file, design_line, - ) + ErrorBuilder::new(ErrorCategory::Class) + .title(format!( + "class \"{}\" from the unit design not found in the C++ class implementation", + entity.id + )) + .field("class", format!("\"{}\"", entity.id)) + .field("design source file", format!("\"{}\"", design_source_file(entity))) + .field("design source line", design_source_line(entity).to_string()) + .fix(format!( + "add implementation class \"{}\" in the C++ class implementation, or remove it from the unit design", + entity.id + )) + .build() } fn format_missing_member( @@ -440,47 +470,97 @@ impl ClassDesignImplementationValidator { member_type: &str, member_name: &str, ) -> String { - let (design_file, design_line) = design_entity.source_location.display(); - format!( - "Missing implementation {member_type} for unit design entity:\n\ - Entity ID : {}\n\ - Member : {}\n\ - Design source file : {}\n\ - Design source line : {}\n\ - Required Action : Implement the member or update the unit design", - design_entity.id, member_name, design_file, design_line, - ) + ErrorBuilder::new(ErrorCategory::Member) + .title(format!( + "{member_type} \"{member_name}\" from entity \"{}\" in the unit design not found in the C++ class implementation", + design_entity.id + )) + .field("entity", format!("\"{}\"", design_entity.id)) + .field("member", format!("{member_type} \"{member_name}\"")) + .field( + "design source file", + format!("\"{}\"", design_source_file(design_entity)), + ) + .field("design source line", design_source_line(design_entity).to_string()) + .fix(format!( + "add {member_type} \"{member_name}\" to entity \"{}\" in the C++ class implementation, or remove it from the unit design", + design_entity.id + )) + .build() } fn format_mismatch( design_entity: &SimpleEntity, implementation_entity: &SimpleEntity, field: &str, + field_reference_style: FieldReferenceStyle, design_value: &str, implementation_value: &str, ) -> String { - let (design_file, design_line) = design_entity.source_location.display(); - let (implement_file, implement_line) = implementation_entity.source_location.display(); - format!( - "Implementation class data differs from unit design entity:\n\ - Entity ID : {}\n\ - Field : {}\n\ - Design value : {}\n\ - Design source file : {}\n\ - Design source line : {}\n\ - Implement value : {}\n\ - Implement source file : {}\n\ - Implement source line : {}\n\ - Required Action : Align the implementation with the unit design or update the unit design", - design_entity.id, - field, - design_value, - design_file, - design_line, - implementation_value, - implement_file, - implement_line - ) + let field_reference = format_field_reference(field, field_reference_style); + + ErrorBuilder::new(ErrorCategory::Implementation) + .title(format!( + "field {field_reference} on entity \"{}\" differs between the unit design and the C++ class implementation", + design_entity.id, + )) + .field("entity", format!("\"{}\"", design_entity.id)) + .field("field", field) + .field("design value", design_value) + .field( + "design source file", + format!("\"{}\"", design_source_file(design_entity)), + ) + .field("design source line", design_source_line(design_entity).to_string()) + .field("implementation value", implementation_value) + .field( + "implementation source file", + format!("\"{}\"", source_file(implementation_entity)), + ) + .field( + "implementation source line", + source_line(implementation_entity).to_string(), + ) + .fix(format!( + "make {field_reference} in entity \"{}\" consistent between the unit design and the C++ class implementation", + design_entity.id + )) + .build() + } +} + +fn format_field_reference(field: &str, style: FieldReferenceStyle) -> String { + match style { + FieldReferenceStyle::Quoted => format!("\"{field}\""), + FieldReferenceStyle::Verbatim => field.to_string(), + } +} + +fn design_source_file(entity: &SimpleEntity) -> String { + source_file(entity) +} + +fn source_file(entity: &SimpleEntity) -> String { + let (file, _) = entity.source_location.display(); + + if file.is_empty() { + "".to_string() + } else { + file + } +} + +fn design_source_line(entity: &SimpleEntity) -> String { + source_line(entity) +} + +fn source_line(entity: &SimpleEntity) -> String { + let (_, line) = entity.source_location.display(); + + if line == 0 { + "".to_string() + } else { + line.to_string() } } @@ -840,8 +920,9 @@ mod tests { use crate::models::ClassDiagramInputs; use class_diagram::{ ClassDiagram, EntityType, FunctionArgument, MemberVariable, Method, RelationType, - Relationship, SimpleEntity, SourceLocation, Visibility, + Relationship, SimpleEntity, Visibility, }; + use source_location::SourceLocation; use std::sync::Once; struct TestLogger; @@ -1073,7 +1154,7 @@ mod tests { assert!(result .failures .iter() - .any(|message| message.contains("Missing implementation class"))); + .any(|message| message.contains("[Class] Class \"Sample\" from the unit design not found in the C++ class implementation."))); } #[test] @@ -1086,7 +1167,7 @@ mod tests { assert!(result .failures .iter() - .any(|message| message.contains("Missing implementation method"))); + .any(|message| message.contains("[Member] Method \"run()\" from entity \"Sample\" in the unit design not found in the C++ class implementation."))); } #[test] @@ -1121,8 +1202,8 @@ mod tests { let result = validate_class_design_implementation(&design, &implementation); assert!(result.failures.iter().any(|message| { - message.contains("Implementation class data differs") - && message.contains("variable \"value_\" data_type") + message.contains("[Implementation] Field variable \"value_\" data type on entity \"Sample\" differs between the unit design and the C++ class implementation.") + && message.contains("variable \"value_\" data type") })); } @@ -1147,7 +1228,7 @@ mod tests { assert!(result .failures .iter() - .all(|message| !message.contains("Missing implementation method"))); + .all(|message| !message.contains("[member] method"))); } #[test] @@ -1215,7 +1296,8 @@ mod tests { let result = validate_class_design_implementation(&design, &implementation); assert!(result.failures.iter().any(|message| { - message.contains("Missing implementation method") && message.contains("stop(int, int)") + message.contains("[Member] Method \"stop(int, int)\" from entity \"Sample\" in the unit design not found in the C++ class implementation.") + && message.contains("stop(int, int)") })); assert!(result .failures @@ -1242,8 +1324,8 @@ mod tests { assert!(result .failures .iter() - .any(|message| message.contains("Implementation class data differs"))); + .any(|message| message.contains("[Implementation] Field"))); assert!(diagnostics.contains("Comparing design entity")); - assert!(!diagnostics.contains("Implementation class data differs")); + assert!(!diagnostics.contains("[Implementation] Field")); } } diff --git a/validation/core/src/validators/component_internal_api_validator.rs b/validation/core/src/validators/component_internal_api_validator.rs index 22bfa6e1..e903557d 100644 --- a/validation/core/src/validators/component_internal_api_validator.rs +++ b/validation/core/src/validators/component_internal_api_validator.rs @@ -14,11 +14,13 @@ //! Validation: compare component-diagram interfaces with interfaces declared //! by the internal API diagram. -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; use super::shared::format_name_list; use crate::models::{ComponentDiagramArchitecture, InternalApiIndex, LogicComponentExt}; +use crate::results::{ErrorBuilder, ErrorCategory}; use crate::{Diagnostics, ValidationResult}; +use source_location::SourceLocation; /// Run component-vs-internal-API interface reference validation. pub fn validate_component_internal_api( @@ -29,7 +31,7 @@ pub fn validate_component_internal_api( } struct ComponentInternalApiValidator { - component_interface_ids: BTreeSet, + component_interface_sources: BTreeMap, internal_api_interface_ids: BTreeSet, result: ValidationResult, } @@ -39,17 +41,23 @@ impl ComponentInternalApiValidator { component_diagram: &ComponentDiagramArchitecture, internal_api_diagram: &InternalApiIndex, ) -> Self { + let component_interface_sources = + collect_component_internal_interface_sources(component_diagram); + Self { - component_interface_ids: collect_component_internal_interface_ids(component_diagram), + component_interface_sources, internal_api_interface_ids: collect_internal_api_interface_ids(internal_api_diagram), result: ValidationResult::default(), } } fn run(mut self) -> ValidationResult { + let component_interface_ids: BTreeSet = + self.component_interface_sources.keys().cloned().collect(); + append_debug_log( &mut self.result.diagnostics, - &self.component_interface_ids, + &component_interface_ids, &self.internal_api_interface_ids, ); self.check_component_interfaces_declared_by_internal_api(); @@ -57,8 +65,9 @@ impl ComponentInternalApiValidator { } fn check_component_interfaces_declared_by_internal_api(&mut self) { - let missing_interfaces: BTreeSet = self - .component_interface_ids + let component_interface_ids: BTreeSet = + self.component_interface_sources.keys().cloned().collect(); + let missing_interfaces: BTreeSet = component_interface_ids .difference(&self.internal_api_interface_ids) .cloned() .collect(); @@ -67,6 +76,7 @@ impl ComponentInternalApiValidator { self.result .add_failure(format_missing_internal_api_interface_error( &missing_interfaces, + &self.component_interface_sources, )); } } @@ -88,14 +98,14 @@ fn append_debug_log( } } -fn collect_component_internal_interface_ids( +fn collect_component_internal_interface_sources( component_diagram: &ComponentDiagramArchitecture, -) -> BTreeSet { +) -> BTreeMap { component_diagram .entities .iter() .filter(|entity| entity.is_interface() && entity.parent_id.is_some()) - .map(|entity| entity.id.clone()) + .map(|entity| (entity.id.clone(), entity.source_location.clone())) .collect() } @@ -108,13 +118,36 @@ fn collect_internal_api_interface_ids(internal_api_diagram: &InternalApiIndex) - fn format_missing_internal_api_interface_error( missing_internal_api_interfaces: &BTreeSet, + component_interface_sources: &BTreeMap, ) -> String { - format!( - "Internal API consistency failure: Missing internal API interface:\n\ - Missing interfaces : {missing_interfaces}\n\ - Action : Add each component interface to the internal API diagram or remove it from the component diagram", - missing_interfaces = format_name_list(missing_internal_api_interfaces), - ) + let missing_interfaces = format_name_list(missing_internal_api_interfaces); + + let mut error = ErrorBuilder::new(ErrorCategory::Interface) + .title(format!( + "component interface(s) {missing_interfaces} from the component diagram not found in the internal API diagram" + )) + .field("missing interfaces", missing_interfaces.clone()); + + for interface_id in missing_internal_api_interfaces { + if let Some(source_location) = component_interface_sources.get(interface_id) { + let (source_file, source_line) = source_location.display(); + error = error + .field( + format!("component source file for \"{interface_id}\""), + format!("\"{source_file}\""), + ) + .field( + format!("component source line for \"{interface_id}\""), + source_line.to_string(), + ); + } + } + + error + .fix(format!( + "add interface declaration(s) {missing_interfaces} in the internal API diagram, or remove those interface declarations from the component diagram" + )) + .build() } #[cfg(test)] diff --git a/validation/core/src/validators/component_public_api_validator.rs b/validation/core/src/validators/component_public_api_validator.rs index f793cd1e..1cb35d2c 100644 --- a/validation/core/src/validators/component_public_api_validator.rs +++ b/validation/core/src/validators/component_public_api_validator.rs @@ -11,14 +11,16 @@ // SPDX-License-Identifier: Apache-2.0 // ******************************************************************************* -//! Validation: compare public API references in the component diagram with +//! Validation: compare public API references in the static design diagram with //! interfaces declared by the public API class diagram. -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; use super::shared::format_name_list; use crate::models::{ComponentDiagramArchitecture, LogicComponentExt, PublicApiIndex}; +use crate::results::{ErrorBuilder, ErrorCategory}; use crate::{Diagnostics, ValidationResult}; +use source_location::SourceLocation; /// Run component-vs-public-API reference validation. pub fn validate_component_public_api( @@ -30,7 +32,7 @@ pub fn validate_component_public_api( struct ComponentPublicApiValidator { /// Public API interfaces explicitly declared in the component diagram. - component_public_api_ids: BTreeSet, + component_public_api_sources: BTreeMap, /// Public API interfaces referenced by relationships from SEooC entities. seooc_related_public_api_ids: BTreeSet, /// Public API interfaces declared in the public API class diagram. @@ -45,16 +47,19 @@ impl ComponentPublicApiValidator { ) -> Self { Self { seooc_related_public_api_ids: collect_seooc_related_public_api_ids(component_diagram), - component_public_api_ids: collect_component_public_api_ids(component_diagram), + component_public_api_sources: collect_component_public_api_sources(component_diagram), design_public_api_ids: public_api_index.api_names().cloned().collect(), result: ValidationResult::default(), } } fn run(mut self) -> ValidationResult { + let component_public_api_ids: BTreeSet = + self.component_public_api_sources.keys().cloned().collect(); + append_debug_log( &mut self.result.diagnostics, - &self.component_public_api_ids, + &component_public_api_ids, &self.seooc_related_public_api_ids, &self.design_public_api_ids, ); @@ -64,28 +69,35 @@ impl ComponentPublicApiValidator { } fn check_component_public_apis_declared_by_public_api(&mut self) { - let missing_public_apis: BTreeSet = self - .component_public_api_ids + let component_public_api_ids: BTreeSet = + self.component_public_api_sources.keys().cloned().collect(); + let missing_public_apis: BTreeSet = component_public_api_ids .difference(&self.design_public_api_ids) .cloned() .collect(); if !missing_public_apis.is_empty() { - self.result - .add_failure(format_missing_public_api_error(&missing_public_apis)); + self.result.add_failure(format_missing_public_api_error( + &missing_public_apis, + &self.component_public_api_sources, + &self.design_public_api_ids, + )); } } fn check_component_public_apis_have_relationship(&mut self) { - let unrelated_public_apis: BTreeSet = self - .component_public_api_ids + let component_public_api_ids: BTreeSet = + self.component_public_api_sources.keys().cloned().collect(); + let unrelated_public_apis: BTreeSet = component_public_api_ids .difference(&self.seooc_related_public_api_ids) .cloned() .collect(); if !unrelated_public_apis.is_empty() { - self.result - .add_failure(format_unrelated_public_api_error(&unrelated_public_apis)); + self.result.add_failure(format_unrelated_public_api_error( + &unrelated_public_apis, + &self.component_public_api_sources, + )); } } } @@ -112,14 +124,14 @@ fn append_debug_log( } } -fn collect_component_public_api_ids( +fn collect_component_public_api_sources( component_diagram: &ComponentDiagramArchitecture, -) -> BTreeSet { +) -> BTreeMap { component_diagram .entities .iter() .filter(|entity| entity.is_interface() && entity.parent_id.is_none()) - .map(|entity| entity.id.clone()) + .map(|entity| (entity.id.clone(), entity.source_location.clone())) .collect() } @@ -142,22 +154,102 @@ fn collect_seooc_related_public_api_ids( .collect() } -fn format_missing_public_api_error(missing_public_apis: &BTreeSet) -> String { - format!( - "Public API consistency failure: Missing public API declaration:\n\ - Missing public APIs : {missing_public_apis}\n\ - Action : Declare each public API interface in the public API class diagram or remove it from the component diagram", - missing_public_apis = format_name_list(missing_public_apis), - ) +fn format_missing_public_api_error( + missing_public_apis: &BTreeSet, + component_public_api_sources: &BTreeMap, + design_public_api_ids: &BTreeSet, +) -> String { + let missing_public_api_names = format_name_list(missing_public_apis); + let case_mismatch_public_apis = + collect_case_mismatch_public_apis(missing_public_apis, design_public_api_ids); + let has_case_mismatch = !case_mismatch_public_apis.is_empty(); + let case_mismatch_names = format_name_list(&case_mismatch_public_apis); + let case_mismatch_title_suffix = if has_case_mismatch { + format!("; use {case_mismatch_names} (case-sensitive)") + } else { + String::new() + }; + let case_mismatch_fix_suffix = if has_case_mismatch { + format!("; if already declared, use {case_mismatch_names} (case-sensitive)") + } else { + String::new() + }; + + let mut error = ErrorBuilder::new(ErrorCategory::Interface) + .title(format!( + "public API interface(s) {missing_public_api_names} from the static diagram not found in the public API diagram{case_mismatch_title_suffix}", + )) + .field("missing public APIs", missing_public_api_names.clone()); + + for interface_id in missing_public_apis { + if let Some(source_location) = component_public_api_sources.get(interface_id) { + let (source_file, source_line) = source_location.display(); + error = error + .field( + format!("static source file for \"{interface_id}\""), + format!("\"{source_file}\""), + ) + .field( + format!("static source line for \"{interface_id}\""), + source_line.to_string(), + ); + } + } + + error + .fix(format!( + "add public API declaration(s) {missing_public_api_names} in the public API diagram, or remove those interface declarations from the static diagram{case_mismatch_fix_suffix}", + )) + .build() +} + +fn collect_case_mismatch_public_apis( + missing_public_apis: &BTreeSet, + design_public_api_ids: &BTreeSet, +) -> BTreeSet { + missing_public_apis + .iter() + .filter(|missing_id| { + design_public_api_ids.iter().any(|design_id| { + design_id != *missing_id && design_id.eq_ignore_ascii_case(missing_id) + }) + }) + .cloned() + .collect() } -fn format_unrelated_public_api_error(unrelated_public_apis: &BTreeSet) -> String { - format!( - "Public API consistency failure: Public API interface has no component relationship:\n\ - Public APIs : {public_apis}\n\ - Action : Connect each public API interface to the SEooC, or remove it from the static design diagram", - public_apis = format_name_list(unrelated_public_apis), - ) +fn format_unrelated_public_api_error( + unrelated_public_apis: &BTreeSet, + component_public_api_sources: &BTreeMap, +) -> String { + let public_api_names = format_name_list(unrelated_public_apis); + + let mut error = ErrorBuilder::new(ErrorCategory::Interface) + .title(format!( + "public API interface(s) {public_api_names} in the static diagram have no relationship to the SEooC" + )) + .field("public APIs", public_api_names.clone()); + + for interface_id in unrelated_public_apis { + if let Some(source_location) = component_public_api_sources.get(interface_id) { + let (source_file, source_line) = source_location.display(); + error = error + .field( + format!("static source file for \"{interface_id}\""), + format!("\"{source_file}\""), + ) + .field( + format!("static source line for \"{interface_id}\""), + source_line.to_string(), + ); + } + } + + error + .fix(format!( + "connect public API interface(s) {public_api_names} from the SEooC boundary in the static diagram, or remove those interface declarations if they are not intended to be public" + )) + .build() } #[cfg(test)] diff --git a/validation/core/src/validators/component_sequence_validator.rs b/validation/core/src/validators/component_sequence_validator.rs index 8b0baf7e..f5897b5e 100644 --- a/validation/core/src/validators/component_sequence_validator.rs +++ b/validation/core/src/validators/component_sequence_validator.rs @@ -21,6 +21,7 @@ use super::shared::{ SequenceCallContext, UnitBindings, }; use crate::models::{ComponentDiagramArchitecture, SequenceDiagramIndex}; +use crate::results::{ErrorBuilder, ErrorCategory}; use crate::{Diagnostics, ValidationResult}; /// Run component-vs-sequence naming validation. @@ -33,10 +34,13 @@ pub fn validate_component_sequence( type ConnectedUnitPairs = BTreeMap<(String, String), BTreeSet>; +const EXTERNAL_ENDPOINT_NAME: &str = "ExternalEndpoint"; + struct ComponentSequenceValidator<'a> { observed_participants: &'a BTreeSet, observed_call_contexts: Vec>, connected_unit_pairs: ConnectedUnitPairs, + sequence_diagram: &'a SequenceDiagramIndex, unit_bindings: UnitBindings, result: ValidationResult, } @@ -88,6 +92,7 @@ impl<'a> ComponentSequenceValidator<'a> { observed_participants: sequence_diagram.used_participants(), observed_call_contexts, connected_unit_pairs: build_connected_unit_pairs(&unit_bindings), + sequence_diagram, unit_bindings, result: ValidationResult::default(), } @@ -117,25 +122,50 @@ impl<'a> ComponentSequenceValidator<'a> { .keys() .filter(|alias| !self.observed_participants.contains(*alias)) { - self.result.add_failure(format!( - "Naming consistency failure: component unit alias not found in sequence participants:\n\ - Unit alias : \"{alias}\"\n\ - Source : Component diagram unit aliases\n\ - Action : Add a matching sequence participant for this unit alias", - )); + let (source_file, source_line) = self + .unit_bindings + .get(alias) + .and_then(|bindings| bindings.source_location.as_ref()) + .map(|source_location| source_location.display()) + .unwrap_or_default(); + + self.result.add_failure( + ErrorBuilder::new(ErrorCategory::Naming) + .title(format!( + "alias \"{alias}\" from the component diagram not found in the sequence diagram" + )) + .field("alias", format!("\"{alias}\"")) + .field("component source file", format!("\"{source_file}\"")) + .field("component source line", source_line.to_string()) + .fix(format!( + "add sequence participant \"{alias}\" in the sequence diagram, or remove it from the component diagram" + )) + .build(), + ); } - for participant in self - .observed_participants - .iter() - .filter(|participant| !self.unit_bindings.contains_key(*participant)) - { - self.result.add_failure(format!( - "Naming consistency failure: sequence participant not found in component unit aliases:\n\ - Participant : \"{participant}\"\n\ - Source : Sequence diagram participants\n\ - Action : Add a matching component unit alias or remove this participant", - )); + for participant in self.observed_participants.iter().filter(|participant| { + !is_external_endpoint(participant) && !self.unit_bindings.contains_key(*participant) + }) { + let (source_file, source_line) = self + .sequence_diagram + .participant_source(participant) + .map(|source_location| source_location.display()) + .unwrap_or_default(); + + self.result.add_failure( + ErrorBuilder::new(ErrorCategory::Naming) + .title(format!( + "participant \"{participant}\" from the sequence diagram not found in the component diagram" + )) + .field("participant", format!("\"{participant}\"")) + .field("sequence source file", format!("\"{source_file}\"")) + .field("sequence source line", source_line.to_string()) + .fix(format!( + "add component unit alias \"{participant}\" in the component diagram, or remove it from the sequence diagram" + )) + .build(), + ); } } @@ -145,14 +175,45 @@ impl<'a> ComponentSequenceValidator<'a> { continue; } - self.result.add_failure(format!( - "Interface consistency failure: interface-connected units are missing a sequence function-call connection:\n\ - Unit pair : {unit_pair}\n\ - Shared interfaces : {shared_interfaces}\n\ - Action : Add a function-call connection between these units in a sequence diagram", - unit_pair = format_unit_pair(left_unit, right_unit), - shared_interfaces = format_name_list(interfaces), - )); + let unit_pair = format_unit_pair(left_unit, right_unit); + let shared_interfaces = format_name_list(interfaces); + let remove_connection_fix = if interfaces.len() == 1 { + "remove that shared interface connection from the component diagram" + } else { + "remove those shared interface connections from the component diagram" + }; + let (left_source_file, left_source_line) = unit_source(&self.unit_bindings, left_unit); + let (right_source_file, right_source_line) = + unit_source(&self.unit_bindings, right_unit); + + self.result.add_failure( + ErrorBuilder::new(ErrorCategory::Interface) + .title(format!( + "component-connected units \"{left_unit}\" and \"{right_unit}\" have no corresponding function-call in the sequence diagram" + )) + .field("unit pair", unit_pair) + .field( + format!("component source file for \"{left_unit}\""), + format!("\"{left_source_file}\""), + ) + .field( + format!("component source line for \"{left_unit}\""), + left_source_line.to_string(), + ) + .field( + format!("component source file for \"{right_unit}\""), + format!("\"{right_source_file}\""), + ) + .field( + format!("component source line for \"{right_unit}\""), + right_source_line.to_string(), + ) + .field("shared interfaces", shared_interfaces.clone()) + .fix(format!( + "add a function-call between \"{left_unit}\" and \"{right_unit}\" in the sequence diagram, or {remove_connection_fix}" + )) + .build(), + ); } } @@ -167,6 +228,10 @@ impl<'a> ComponentSequenceValidator<'a> { let mut seen_pairs = BTreeSet::new(); for call_context in &self.observed_call_contexts { + if call_involves_external_endpoint(call_context) { + continue; + } + if call_context.caller_unit == call_context.callee_unit { continue; } @@ -185,25 +250,40 @@ impl<'a> ComponentSequenceValidator<'a> { continue; } - self.result.add_failure(format!( - "Interface consistency failure: sequence-connected units have no corresponding shared interface connection in the component diagram:\n\ - Unit pair : {unit_pair}\n\ - Interfaces for \"{left_unit}\" : {left_interfaces}\n\ - Interfaces for \"{right_unit}\" : {right_interfaces}\n\ - Action : Add a shared interface relation between these units in the component diagram", - unit_pair = format_unit_pair( - call_context.normalized_left_unit(), - call_context.normalized_right_unit(), - ), - left_unit = call_context.normalized_left_unit(), - right_unit = call_context.normalized_right_unit(), - left_interfaces = format_name_list(left_interfaces), - right_interfaces = format_name_list(right_interfaces), - )); + let left_unit = call_context.normalized_left_unit(); + let right_unit = call_context.normalized_right_unit(); + let unit_pair = format_unit_pair(left_unit, right_unit); + let left_interface_label = format!("interfaces for \"{left_unit}\""); + let right_interface_label = format!("interfaces for \"{right_unit}\""); + let (source_file, source_line) = sequence_call_source(call_context); + + self.result.add_failure( + ErrorBuilder::new(ErrorCategory::Interface) + .title(format!( + "sequence-connected units \"{left_unit}\" and \"{right_unit}\" have no corresponding shared interface connection in the component diagram." + )) + .field("unit pair", unit_pair) + .field("sequence source file", format!("\"{source_file}\"")) + .field("sequence source line", source_line.to_string()) + .field(&left_interface_label, format_name_list(left_interfaces)) + .field(&right_interface_label, format_name_list(right_interfaces)) + .fix(format!( + "add a shared interface connection between \"{left_unit}\" and \"{right_unit}\" in the component diagram, or remove that function-call from the sequence diagram" + )) + .build(), + ); } } } +fn is_external_endpoint(participant: &str) -> bool { + participant == EXTERNAL_ENDPOINT_NAME +} + +fn call_involves_external_endpoint(call_context: &SequenceCallContext<'_>) -> bool { + is_external_endpoint(call_context.caller_unit) || is_external_endpoint(call_context.callee_unit) +} + fn append_debug_log( diagnostics: &mut Diagnostics, observed_participants: &BTreeSet, @@ -280,6 +360,18 @@ fn build_connected_unit_pairs( connected_unit_pairs } +fn unit_source(unit_bindings: &UnitBindings, unit_alias: &str) -> (String, u32) { + unit_bindings + .get(unit_alias) + .and_then(|bindings| bindings.source_location.as_ref()) + .map(|source_location| source_location.display()) + .unwrap_or_default() +} + +fn sequence_call_source(call_context: &SequenceCallContext<'_>) -> (String, u32) { + call_context.source_location.display() +} + fn format_unit_pair(left_unit: &str, right_unit: &str) -> String { format!("\"{left_unit}\" <-> \"{right_unit}\"") } diff --git a/validation/core/src/validators/sequence_internal_api_validator.rs b/validation/core/src/validators/sequence_internal_api_validator.rs index 370936ad..b4bb3ed0 100644 --- a/validation/core/src/validators/sequence_internal_api_validator.rs +++ b/validation/core/src/validators/sequence_internal_api_validator.rs @@ -25,6 +25,7 @@ use crate::models::{ ComponentDiagramArchitecture, InternalApiIndex, InternalApiInterface, LogicComponentExt, SequenceDiagramIndex, }; +use crate::results::{ErrorBuilder, ErrorCategory}; use crate::{Diagnostics, ValidationResult}; /// Run sequence-vs-internal-API method and coverage validation. @@ -159,7 +160,6 @@ impl<'a> SequenceInternalApiValidator<'a> { call_context, method_name, "sequence function name was not found in available interface methods", - "Declare this method on one of the available interfaces in the internal API diagram", )); } @@ -204,7 +204,6 @@ impl<'a> SequenceInternalApiValidator<'a> { call_context, method_name, "sequence function name was not found in the related interface methods", - "Declare this method on a shared interface referenced by both participating units in the internal API diagram", )); } @@ -456,37 +455,49 @@ fn format_interface_method_coverage_error( interface: &InternalApiInterface, missing_methods: &BTreeSet, ) -> String { - format!( - "Coverage consistency failure: internal API interface functions are not exercised in sequence diagrams:\n\ - Interface id : \"{interface_id}\"\n\ - Missing functions : {missing_functions}\n\ - Action : Add sequence interactions that call each missing function", - interface_id = interface.id, - missing_functions = format_name_list(missing_methods), - ) + let missing_functions = format_name_list(missing_methods); + let (source_file, source_line) = interface.source_location.display(); + + ErrorBuilder::new(ErrorCategory::Coverage) + .title(format!( + "methods {missing_functions} declared on internal API interface \"{}\" in the internal API diagram are not exercised in the sequence diagram", + interface.id + )) + .field("interface id", format!("\"{}\"", interface.id)) + .field("internal API source file", format!("\"{source_file}\"")) + .field("internal API source line", source_line.to_string()) + .field("missing functions", missing_functions.clone()) + .fix(format!( + "add sequence interactions for functions {missing_functions} in the sequence diagram, or remove function declarations {missing_functions} in internal API interface \"{}\"", + interface.id + )) + .build() } fn format_sequence_method_consistency_error( call_context: &SequenceCallContext<'_>, method_name: &str, description: &str, - action: &str, ) -> String { let sequence_call = format_sequence_call( call_context.caller_unit, call_context.callee_unit, method_name, ); - - format!( - "Method consistency failure: {description}:\n\ - Sequence call : {sequence_call}\n\ - Source file : \"{source_file}\"\n\ - Source line : {source_line}\n\ - Action : {action}", - source_file = call_context.source_file, - source_line = call_context.source_line, - ) + let (source_file, source_line) = call_context.source_location.display(); + + ErrorBuilder::new(ErrorCategory::Method) + .title(format!( + "sequence function \"{method_name}\" from sequence call {sequence_call} in the sequence diagram not found in the internal API diagram" + )) + .field("sequence call", sequence_call.clone()) + .field("sequence source file", format!("\"{source_file}\"")) + .field("sequence source line", source_line.to_string()) + .field("detail", description) + .fix(format!( + "add method \"{method_name}\" in a matching internal API interface in the internal API diagram, or remove sequence function \"{method_name}\" in sequence call {sequence_call} in the sequence diagram" + )) + .build() } fn format_sequence_role_consistency_error( @@ -500,20 +511,34 @@ fn format_sequence_role_consistency_error( method_name, ); - format!( - "Interface consistency failure: sequence interaction does not match consumer/provider roles in the component diagram:\n\ - Sequence call : {sequence_call}\n\ - Source file : \"{source_file}\"\n\ - Source line : {source_line}\n\ - Expected caller role: \"{caller_unit}\" should require shared interface(s) {expected_interfaces}\n\ - Expected callee role: \"{callee_unit}\" should provide shared interface(s) {expected_interfaces}\n\ - Action : Reverse the sequence call or align the required/provided interface bindings in the component diagram", - caller_unit = call_context.caller_unit, - callee_unit = call_context.callee_unit, - expected_interfaces = format_name_list(expected_interfaces), - source_file = call_context.source_file, - source_line = call_context.source_line, - ) + let expected_interfaces = format_name_list(expected_interfaces); + let (source_file, source_line) = call_context.source_location.display(); + + ErrorBuilder::new(ErrorCategory::Interface) + .title(format!( + "sequence call {sequence_call} in the sequence diagram does not match the required/provided interface roles in the component diagram" + )) + .field("sequence call", sequence_call.clone()) + .field("sequence source file", format!("\"{source_file}\"")) + .field("sequence source line", source_line.to_string()) + .field( + "expected caller role", + format!( + "\"{}\" should require shared interface(s) {}", + call_context.caller_unit, expected_interfaces + ), + ) + .field( + "expected callee role", + format!( + "\"{}\" should provide shared interface(s) {}", + call_context.callee_unit, expected_interfaces + ), + ) + .fix(format!( + "add required/provided interface bindings for shared interface(s) {expected_interfaces} in the component diagram, or remove sequence call {sequence_call} in the sequence diagram" + )) + .build() } #[cfg(test)] diff --git a/validation/core/src/validators/shared/diagram_analysis.rs b/validation/core/src/validators/shared/diagram_analysis.rs index 8e7bb8ab..14bc5a52 100644 --- a/validation/core/src/validators/shared/diagram_analysis.rs +++ b/validation/core/src/validators/shared/diagram_analysis.rs @@ -15,6 +15,8 @@ use std::collections::{BTreeMap, BTreeSet}; +use source_location::SourceLocation; + use crate::models::{ ComponentDiagramArchitecture, ComponentRelationType, EndpointRole, LogicComponentExt, ObservedSequenceCall, @@ -24,6 +26,7 @@ pub(in crate::validators) type UnitBindings = BTreeMap; #[derive(Clone, Default)] pub(in crate::validators) struct UnitInterfaces { + pub(in crate::validators) source_location: Option, pub(in crate::validators) all_interfaces: BTreeSet, pub(in crate::validators) required_interfaces: BTreeSet, pub(in crate::validators) provided_interfaces: BTreeSet, @@ -34,8 +37,7 @@ pub(in crate::validators) struct SequenceCallContext<'a> { pub(in crate::validators) caller_unit: &'a str, pub(in crate::validators) callee_unit: &'a str, pub(in crate::validators) method: &'a str, - pub(in crate::validators) source_file: &'a str, - pub(in crate::validators) source_line: u32, + pub(in crate::validators) source_location: &'a SourceLocation, pub(in crate::validators) caller_interfaces: BTreeSet, pub(in crate::validators) callee_interfaces: BTreeSet, } @@ -66,7 +68,10 @@ pub(in crate::validators) fn build_unit_bindings( continue; }; - let mut bindings = UnitInterfaces::default(); + let mut bindings = UnitInterfaces { + source_location: Some(entity.source_location.clone()), + ..UnitInterfaces::default() + }; for relation in &entity.relations { if !interface_ids.contains(relation.target.as_str()) { @@ -122,8 +127,7 @@ pub(in crate::validators) fn build_observed_call_contexts<'a>( method: call.method.as_str(), caller_interfaces, callee_interfaces, - source_file: call.source_file.as_str(), - source_line: call.source_line, + source_location: &call.source_location, } }) .collect() diff --git a/validation/core/src/validators/test/component_internal_api_validator_test.rs b/validation/core/src/validators/test/component_internal_api_validator_test.rs index 64c410ad..060833d5 100644 --- a/validation/core/src/validators/test/component_internal_api_validator_test.rs +++ b/validation/core/src/validators/test/component_internal_api_validator_test.rs @@ -41,9 +41,9 @@ fn reports_missing_component_interface_declared_by_internal_api() { let validation_result = validate(component_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0].contains("Missing internal API interface")); - assert!(validation_result.failures[0] - .contains("Missing interfaces : \"component_example.InternalInterface\"")); + assert!(validation_result.failures[0].contains( + "Component interface(s) \"component_example.InternalInterface\" from the component diagram not found in the internal API diagram." + )); assert!(!validation_result.failures[0].contains("Unit :")); } @@ -63,9 +63,9 @@ fn reports_each_missing_component_interface_once() { let validation_result = validate(component_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0].contains("Missing internal API interface")); - assert!(validation_result.failures[0] - .contains("Missing interfaces : \"component_example.InternalInterface1\"")); + assert!(validation_result.failures[0].contains( + "Component interface(s) \"component_example.InternalInterface1\" from the component diagram not found in the internal API diagram." + )); } #[test] @@ -79,9 +79,9 @@ fn reports_missing_component_interface_even_without_unit_relation() { let validation_result = validate(component_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0].contains("Missing internal API interface")); - assert!(validation_result.failures[0] - .contains("Missing interfaces : \"component_example.UnusedInterface\"")); + assert!(validation_result.failures[0].contains( + "Component interface(s) \"component_example.UnusedInterface\" from the component diagram not found in the internal API diagram." + )); } #[test] @@ -101,9 +101,9 @@ fn reports_all_missing_component_interfaces_in_one_message() { let validation_result = validate(component_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0].contains("Missing internal API interface")); - assert!(validation_result.failures[0] - .contains("Missing interfaces : \"component_example.InternalInterface1\"")); + assert!(validation_result.failures[0].contains( + "Component interface(s) \"component_example.InternalInterface1\" from the component diagram not found in the internal API diagram." + )); } #[test] @@ -117,9 +117,9 @@ fn reports_missing_component_interface_without_sequence_method_call() { let validation_result = validate(component_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0].contains("Missing internal API interface")); - assert!(validation_result.failures[0] - .contains("Missing interfaces : \"component_example.InternalInterface\"")); + assert!(validation_result.failures[0].contains( + "Component interface(s) \"component_example.InternalInterface\" from the component diagram not found in the internal API diagram." + )); } #[test] @@ -134,9 +134,9 @@ fn reports_case_mismatch_between_component_and_internal_api_interface_names() { let validation_result = validate(component_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0].contains("Missing internal API interface")); - assert!(validation_result.failures[0] - .contains("Missing interfaces : \"component_example.InternalInterface\"")); + assert!(validation_result.failures[0].contains( + "Component interface(s) \"component_example.InternalInterface\" from the component diagram not found in the internal API diagram." + )); } #[test] diff --git a/validation/core/src/validators/test/component_public_api_validator_test.rs b/validation/core/src/validators/test/component_public_api_validator_test.rs index 77e4737e..dfdfaf89 100644 --- a/validation/core/src/validators/test/component_public_api_validator_test.rs +++ b/validation/core/src/validators/test/component_public_api_validator_test.rs @@ -85,8 +85,9 @@ fn reports_missing_component_public_api_declaration() { let validation_result = validate(component_diagrams, &public_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0].contains("Missing public API declaration")); - assert!(validation_result.failures[0].contains("Missing public APIs : \"SampleLibraryAPI\"")); + assert!(validation_result.failures[0].contains( + "[Interface] Public API interface(s) \"SampleLibraryAPI\" from the static diagram not found in the public API diagram." + )); } #[test] @@ -100,8 +101,9 @@ fn reports_public_api_without_seooc_relationship() { let validation_result = validate(component_diagrams, &public_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0] - .contains("Public API interface has no component relationship")); + assert!(validation_result.failures[0].contains( + "[Interface] Public API interface(s) \"SampleLibraryAPI\" in the static diagram have no relationship to the SEooC." + )); assert!(validation_result.failures[0].contains("\"SampleLibraryAPI\"")); } @@ -125,8 +127,9 @@ fn ignores_component_relationships_when_checking_public_api() { let validation_result = validate(component_diagrams, &public_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0] - .contains("Public API interface has no component relationship")); + assert!(validation_result.failures[0].contains( + "[Interface] Public API interface(s) \"SampleLibraryAPI\" in the static diagram have no relationship to the SEooC." + )); assert!(validation_result.failures[0].contains("\"SampleLibraryAPI\"")); } diff --git a/validation/core/src/validators/test/component_sequence_validator_test.rs b/validation/core/src/validators/test/component_sequence_validator_test.rs index 64986ef5..f3b4e023 100644 --- a/validation/core/src/validators/test/component_sequence_validator_test.rs +++ b/validation/core/src/validators/test/component_sequence_validator_test.rs @@ -56,12 +56,12 @@ fn reports_missing_and_extra() { let missing_count = validation_result .failures .iter() - .filter(|msg| msg.contains("unit alias not found in sequence participants")) + .filter(|msg| msg.contains("from the component diagram not found in the sequence diagram")) .count(); let unexpected_count = validation_result .failures .iter() - .filter(|msg| msg.contains("sequence participant not found in component unit aliases")) + .filter(|msg| msg.contains("from the sequence diagram not found in the component diagram")) .count(); assert_eq!(missing_count, 2); @@ -123,12 +123,12 @@ fn reports_missing_component_alias_and_interface_connection_for_sequence_call() assert_eq!(validation_result.failures.len(), 2); assert!(validation_result.failures.iter().any(|message| { - message.contains("sequence participant not found in component unit aliases") + message.contains("from the sequence diagram not found in the component diagram") && message.contains("\"orphan\"") })); assert!(validation_result.failures.iter().any(|message| { message - .contains("sequence-connected units have no corresponding shared interface connection") + .contains("have no corresponding shared interface connection in the component diagram") && message.contains("\"u1\"") && message.contains("\"orphan\"") && message.contains("\"InternalInterface\"") @@ -148,7 +148,7 @@ fn reports_missing_sequence_call_for_interface_connected_units() { assert_eq!(validation_result.failures.len(), 1); assert!(validation_result.failures[0] - .contains("interface-connected units are missing a sequence function-call connection")); + .contains("have no corresponding function-call in the sequence diagram")); assert!(validation_result.failures[0].contains("\"InternalInterface\"")); } @@ -165,12 +165,11 @@ fn reports_missing_participant_and_missing_sequence_call_for_interface_connected assert_eq!(validation_result.failures.len(), 2); assert!(validation_result.failures.iter().any(|message| { - message.contains("component unit alias not found in sequence participants") + message.contains("from the component diagram not found in the sequence diagram") && message.contains("\"u2\"") })); assert!(validation_result.failures.iter().any(|message| { - message - .contains("interface-connected units are missing a sequence function-call connection") + message.contains("have no corresponding function-call in the sequence diagram") && message.contains("\"InternalInterface\"") })); } @@ -189,7 +188,7 @@ fn reports_sequence_call_without_corresponding_shared_interface_connection() { assert_eq!(validation_result.failures.len(), 1); assert!(validation_result.failures[0] - .contains("sequence-connected units have no corresponding shared interface connection")); + .contains("have no corresponding shared interface connection in the component diagram")); assert!(validation_result.failures[0].contains("\"CallerInterface\"")); assert!(validation_result.failures[0].contains("\"CalleeInterface\"")); } diff --git a/validation/core/src/validators/test/fixtures.rs b/validation/core/src/validators/test/fixtures.rs index f9941fd0..f07eb517 100644 --- a/validation/core/src/validators/test/fixtures.rs +++ b/validation/core/src/validators/test/fixtures.rs @@ -16,8 +16,10 @@ use crate::models::{ LogicComponent, LogicRelation, SequenceDiagramInputs, }; use class_diagram::{ClassDiagram, EntityType, Method, SimpleEntity, Visibility}; -use component_diagram::SourceLocation; -use sequence_logic::{Event, Interaction, SequenceNode, SequenceTree}; +use sequence_logic::{ + Event, Interaction, ParticipantType, SequenceNode, SequenceParticipant, SequenceTree, +}; +use source_location::SourceLocation; // Common fixtures @@ -131,7 +133,7 @@ pub(super) fn sequence_calls(calls: &[(&str, &str, &str)]) -> SequenceDiagramInp SequenceDiagramInputs { diagrams: vec![SequenceTree { name: Some("seq".to_string()), - participants: Vec::new(), + participants: sequence_participants(calls), root_interactions: calls .iter() .map(|(caller, callee, method)| SequenceNode { @@ -148,6 +150,33 @@ pub(super) fn sequence_calls(calls: &[(&str, &str, &str)]) -> SequenceDiagramInp } } +fn sequence_participants(calls: &[(&str, &str, &str)]) -> Vec { + let mut participants: Vec = Vec::new(); + + for name in calls + .iter() + .flat_map(|(caller, callee, _)| [*caller, *callee]) + { + if name.is_empty() + || participants + .iter() + .any(|participant| participant.display_name == name) + { + continue; + } + + participants.push(SequenceParticipant { + display_name: name.to_string(), + alias: Some(name.to_string()), + participant_type: ParticipantType::Participant, + source_location: dummy_source_location(), + stereotype: None, + }); + } + + participants +} + // Class diagram API fixtures. pub(super) fn internal_api_index(interfaces: Vec<(&str, Vec<&str>)>) -> InternalApiIndex { diff --git a/validation/core/src/validators/test/sequence_internal_api_validator_test.rs b/validation/core/src/validators/test/sequence_internal_api_validator_test.rs index b7a63bc5..a58752cd 100644 --- a/validation/core/src/validators/test/sequence_internal_api_validator_test.rs +++ b/validation/core/src/validators/test/sequence_internal_api_validator_test.rs @@ -79,14 +79,15 @@ fn reports_internal_api_interface_function_not_exercised_without_method_name_che let validation_result = validate(sequence_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0] - .contains("internal API interface functions are not exercised in sequence diagrams")); + assert!(validation_result.failures[0].contains( + "Methods \"OtherMethod\" declared on internal API interface \"InternalInterface\" in the internal API diagram are not exercised in the sequence diagram." + )); assert!(validation_result.failures[0].contains("\"InternalInterface\"")); assert!(validation_result.failures[0].contains("\"OtherMethod\"")); assert!(validation_result .failures .iter() - .all(|message| !message.contains("Method consistency failure"))); + .all(|message| !message.contains("[Method]"))); } #[test] @@ -97,8 +98,9 @@ fn reports_internal_api_interface_function_not_exercised() { let validation_result = validate(sequence_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0] - .contains("internal API interface functions are not exercised in sequence diagrams")); + assert!(validation_result.failures[0].contains( + "Methods \"SetData\" declared on internal API interface \"InternalInterface\" in the internal API diagram are not exercised in the sequence diagram." + )); assert!(validation_result.failures[0].contains("\"InternalInterface\"")); assert!(validation_result.failures[0].contains("\"SetData\"")); } @@ -128,11 +130,14 @@ fn reports_sequence_function_missing_from_available_interfaces_with_component_co assert_eq!(validation_result.failures.len(), 2); assert!(validation_result.failures.iter().any(|message| { - message.contains("sequence function name was not found in available interface methods") - && message.contains("Sequence call : \"u1\" -> \"u2\" : \"GetData\"") + message.contains( + "Sequence function \"GetData\" from sequence call \"u1\" -> \"u2\" : \"GetData\" in the sequence diagram not found in the internal API diagram." + ) && message.contains("sequence function name was not found in available interface methods") })); assert!(validation_result.failures.iter().any(|message| { - message.contains("internal API interface functions are not exercised in sequence diagrams") + message.contains( + "Methods \"OtherMethod\" declared on internal API interface \"InternalInterface\" in the internal API diagram are not exercised in the sequence diagram." + ) && message.contains("\"InternalInterface\"") && message.contains("\"OtherMethod\"") })); @@ -153,15 +158,18 @@ fn reports_sequence_function_missing_when_shared_interface_has_no_direction_role assert_eq!(validation_result.failures.len(), 2); assert!(validation_result.failures.iter().any(|message| { - message.contains("sequence function name was not found in available interface methods") - && message.contains("Sequence call : \"u1\" -> \"u2\" : \"GetData\"") + message.contains( + "Sequence function \"GetData\" from sequence call \"u1\" -> \"u2\" : \"GetData\" in the sequence diagram not found in the internal API diagram." + ) && message.contains("sequence function name was not found in available interface methods") })); assert!(validation_result .failures .iter() - .all(|message| !message.contains("consumer/provider roles"))); + .all(|message| !message.contains("required/provided interface roles"))); assert!(validation_result.failures.iter().all(|message| { - !message.contains("sequence function name was not found in the related interface methods") + !message.contains( + "Detail : sequence function name was not found in the related interface methods", + ) })); } @@ -179,8 +187,9 @@ fn reports_interface_function_not_exercised_in_sequence_diagrams_with_component_ validate_with_component_context(component_diagrams, sequence_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0] - .contains("internal API interface functions are not exercised in sequence diagrams")); + assert!(validation_result.failures[0].contains( + "Methods \"SetData\" declared on internal API interface \"InternalInterface\" in the internal API diagram are not exercised in the sequence diagram." + )); assert!(validation_result.failures[0].contains("\"InternalInterface\"")); assert!(validation_result.failures[0].contains("\"SetData\"")); } @@ -202,8 +211,9 @@ fn reports_unreferenced_internal_api_interface_function_not_exercised_without_se validate_with_component_context(component_diagrams, sequence_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0] - .contains("internal API interface functions are not exercised in sequence diagrams")); + assert!(validation_result.failures[0].contains( + "Methods \"SetData\" declared on internal API interface \"OtherInterface\" in the internal API diagram are not exercised in the sequence diagram." + )); assert!(validation_result.failures[0].contains("\"OtherInterface\"")); assert!(validation_result.failures[0].contains("\"SetData\"")); } @@ -222,8 +232,9 @@ fn reports_self_call_method_mismatch_when_unit_has_missing_internal_api_interfac assert_eq!(validation_result.failures.len(), 1); assert!(validation_result.failures.iter().any(|message| { - message.contains("sequence function name was not found") - && message.contains("Sequence call : \"u1\" -> \"u1\" : \"GetData\"") + message.contains( + "Sequence function \"GetData\" from sequence call \"u1\" -> \"u1\" : \"GetData\" in the sequence diagram not found in the internal API diagram." + ) && message.contains("\"u1\" -> \"u1\" : \"GetData\"") })); } @@ -254,11 +265,14 @@ fn reports_self_call_function_missing_from_available_interfaces() { assert_eq!(validation_result.failures.len(), 2); assert!(validation_result.failures.iter().any(|message| { - message.contains("sequence function name was not found") - && message.contains("Sequence call : \"u1\" -> \"u1\" : \"GetData\"") + message.contains( + "Sequence function \"GetData\" from sequence call \"u1\" -> \"u1\" : \"GetData\" in the sequence diagram not found in the internal API diagram." + ) && message.contains("\"u1\" -> \"u1\" : \"GetData\"") })); assert!(validation_result.failures.iter().any(|message| { - message.contains("internal API interface functions are not exercised in sequence diagrams") + message.contains( + "Methods \"OtherMethod\" declared on internal API interface \"InternalInterface\" in the internal API diagram are not exercised in the sequence diagram." + ) && message.contains("\"InternalInterface\"") && message.contains("\"OtherMethod\"") })); @@ -301,9 +315,10 @@ fn reports_self_call_without_any_available_interfaces() { validate_with_component_context(component_diagrams, sequence_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0].contains("sequence function name was not found")); - assert!(validation_result.failures[0] - .contains("Sequence call : \"u1\" -> \"u1\" : \"GetData\"")); + assert!(validation_result.failures[0].contains( + "Sequence function \"GetData\" from sequence call \"u1\" -> \"u1\" : \"GetData\" in the sequence diagram not found in the internal API diagram." + )); + assert!(validation_result.failures[0].contains("\"u1\" -> \"u1\" : \"GetData\"")); } #[test] @@ -325,11 +340,14 @@ fn reports_method_declared_only_on_caller_side_interfaces() { assert_eq!(validation_result.failures.len(), 2); assert!(validation_result.failures.iter().any(|message| { - message.contains("sequence function name was not found in the related interface methods") - && message.contains("Sequence call : \"u1\" -> \"u2\" : \"GetData\"") + message.contains( + "Sequence function \"GetData\" from sequence call \"u1\" -> \"u2\" : \"GetData\" in the sequence diagram not found in the internal API diagram." + ) && message.contains("sequence function name was not found in the related interface methods") })); assert!(validation_result.failures.iter().any(|message| { - message.contains("internal API interface functions are not exercised in sequence diagrams") + message.contains( + "Methods \"OtherMethod\" declared on internal API interface \"SharedInterface\" in the internal API diagram are not exercised in the sequence diagram." + ) && message.contains("\"SharedInterface\"") && message.contains("\"OtherMethod\"") })); @@ -358,11 +376,14 @@ fn reports_method_declared_only_on_callee_side_interfaces() { assert_eq!(validation_result.failures.len(), 2); assert!(validation_result.failures.iter().any(|message| { - message.contains("sequence function name was not found in the related interface methods") - && message.contains("Sequence call : \"u1\" -> \"u2\" : \"GetData\"") + message.contains( + "Sequence function \"GetData\" from sequence call \"u1\" -> \"u2\" : \"GetData\" in the sequence diagram not found in the internal API diagram." + ) && message.contains("sequence function name was not found in the related interface methods") })); assert!(validation_result.failures.iter().any(|message| { - message.contains("internal API interface functions are not exercised in sequence diagrams") + message.contains( + "Methods \"OtherMethod\" declared on internal API interface \"SharedInterface\" in the internal API diagram are not exercised in the sequence diagram." + ) && message.contains("\"SharedInterface\"") && message.contains("\"OtherMethod\"") })); @@ -393,11 +414,14 @@ fn reports_method_declared_on_both_sides_but_not_on_shared_interface() { assert_eq!(validation_result.failures.len(), 2); assert!(validation_result.failures.iter().any(|message| { - message.contains("sequence function name was not found in the related interface methods") - && message.contains("Sequence call : \"u1\" -> \"u2\" : \"GetData\"") + message.contains( + "Sequence function \"GetData\" from sequence call \"u1\" -> \"u2\" : \"GetData\" in the sequence diagram not found in the internal API diagram." + ) && message.contains("sequence function name was not found in the related interface methods") })); assert!(validation_result.failures.iter().any(|message| { - message.contains("internal API interface functions are not exercised in sequence diagrams") + message.contains( + "Methods \"OtherMethod\" declared on internal API interface \"SharedInterface\" in the internal API diagram are not exercised in the sequence diagram." + ) && message.contains("\"SharedInterface\"") && message.contains("\"OtherMethod\"") })); @@ -421,17 +445,15 @@ fn reports_role_violation_when_method_exists_only_on_reverse_direction_interface validate_with_component_context(component_diagrams, sequence_diagrams, &internal_api); assert_eq!(validation_result.failures.len(), 1); - assert!(validation_result.failures[0] - .contains("sequence interaction does not match consumer/provider roles")); - assert!(validation_result.failures[0] - .contains("Sequence call : \"u1\" -> \"u2\" : \"GetData\"")); - assert!(validation_result.failures[0].contains( - "Expected caller role: \"u1\" should require shared interface(s) \"InternalInterface\"" - )); assert!(validation_result.failures[0].contains( - "Expected callee role: \"u2\" should provide shared interface(s) \"InternalInterface\"" + "Sequence call \"u1\" -> \"u2\" : \"GetData\" in the sequence diagram does not match the required/provided interface roles in the component diagram." )); - assert!(!validation_result.failures[0].contains("Method consistency failure")); + assert!(validation_result.failures[0].contains("\"u1\" -> \"u2\" : \"GetData\"")); + assert!(validation_result.failures[0] + .contains("\"u1\" should require shared interface(s) \"InternalInterface\"")); + assert!(validation_result.failures[0] + .contains("\"u2\" should provide shared interface(s) \"InternalInterface\"")); + assert!(!validation_result.failures[0].contains("[Method]")); } #[test]