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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions tools/metamodel/common/source_location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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<String> {
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)
Expand Down
2 changes: 2 additions & 0 deletions validation/core/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
36 changes: 14 additions & 22 deletions validation/core/integration_test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 |
Expand All @@ -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

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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 |
Expand All @@ -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

Expand All @@ -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
Expand All @@ -253,16 +246,15 @@ 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
suite.

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
Expand Down
4 changes: 4 additions & 0 deletions validation/core/integration_test/bazel_component/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand All @@ -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() {
Expand Down Expand Up @@ -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");
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
Original file line number Diff line number Diff line change
@@ -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": []
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@

@startuml component_diagram

package "Package A" as package_a {
component "Component A" as component_a <<component>> {
component "Unit 1" as Unit_1 <<unit>>
}
package "Sample Seooc" as safety_software_seooc_example <<SEooC>> {
component "Component Example" as component_example <<component>>
}

@enduml
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
Original file line number Diff line number Diff line change
@@ -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": []
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

@startuml component_diagram

package "Package A" as package_a {
component "Component A" as component_a <<component>> {
package "Sample Seooc" as safety_software_seooc_example <<SEooC>> {
component "Component Example" as component_example <<component>> {
component "Unit 1" as unit_1 <<unit>>
}
}
Expand Down
Loading
Loading