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
6 changes: 5 additions & 1 deletion bazel/rules/rules_score/trlc/config/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# *******************************************************************************

load("@trlc//:trlc.bzl", "trlc_requirements", "trlc_requirements_test", "trlc_specification")
load("//bazel/rules/rules_score/trlc/config/test:trlc_check_test.bzl", "trlc_check_test")

trlc_specification(
name = "score_requirements_model",
Expand All @@ -21,7 +22,10 @@ trlc_specification(
visibility = ["//visibility:public"],
)

trlc_requirements_test(
# The model contains ASIL checks that crash TRLC 3.0.0's VCG (CVC5 backend)
# when --verify is used. Use trlc_check_test (no --verify) so the model parses
# and checks evaluate at runtime without triggering the VCG crash.
trlc_check_test(
name = "score_requirements_model_test",
reqs = [
":score_requirements_model",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,20 @@ tuple AssumedSystemReqId {
version Integer
}

// FeatReqSourceId uses the abstract RequirementSafety item type (instead of the
// concrete AssumedSystemReq) so that TRLC can resolve the inherited `safety`
// field in checks FeatReq. At runtime every item is still an AssumedSystemReq
// instance; the broader declared type is only needed to satisfy TRLC 3.0.0's
// field-access resolution rules.
tuple FeatReqSourceId {
item RequirementSafety
separator @
version Integer
}

type FeatReq "High-level feature requirement derived from one or more AssumedSystemReq items." extends RequirementSafety {
derived_from "One or more versioned references to the AssumedSystemReq items this feature requirement is derived from."
AssumedSystemReqId[1 .. *]
FeatReqSourceId[1 .. *]
}

tuple FeatReqId {
Expand Down Expand Up @@ -182,3 +193,33 @@ tuple Measure {

// abstract type StdReq extends Requirement {
// }

///////////////////////////////
// Safety Checks
///////////////////////////////

// Note: use trlc_check_test (no --verify) for these checks; TRLC 3.0.0's VCG crashes on forall over union/abstract tuple item fields.
// ASIL ordering: QM < B < D. If Asil gains new levels, revisit every branch below.

// FeatReq: ASIL must not decrease from the upstream AssumedSystemReq.
// FeatReqSourceId.item is RequirementSafety (abstract) so TRLC resolves the
// inherited `safety` field correctly in this check block.
checks FeatReq {
(forall upstream in derived_from =>
not ((upstream.item.safety == Asil.B and safety == Asil.QM) or
(upstream.item.safety == Asil.D and safety != Asil.D))),
error "ASIL level of derived requirement must be at least the same as the upstream requirement",
safety
}

// CompReq: ASIL must not decrease from any upstream FeatReq or AssumedSystemReq.
// CompReqSourceId.item is a union [FeatReq, AssumedSystemReq]; TRLC resolves
// the `safety` field via the common ancestor RequirementSafety.
checks CompReq {
derived_from == null or
(forall upstream in derived_from =>
not ((upstream.item.safety == Asil.B and safety == Asil.QM) or
(upstream.item.safety == Asil.D and safety != Asil.D))),
error "ASIL level of derived requirement must be at least the same as the upstream requirement",
safety
}
123 changes: 123 additions & 0 deletions bazel/rules/rules_score/trlc/config/test/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# *******************************************************************************
# 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("@trlc//:trlc.bzl", "trlc_requirements")
load(":trlc_check_test.bzl", "trlc_check_test")

# ==============================================================================
# ASIL Safety Check — Test Fixtures
#
# Scenario matrix:
#
# Upstream ASIL | Downstream ASIL | Result
# ---------------+-------------------+--------
# QM | QM | PASS (same level)
# B | B | PASS (same level)
# B | D | PASS (raised level)
# D | D | PASS (same level)
# B | QM | FAIL (degraded — check fires)
# D | B | FAIL (degraded — check fires)
# D | QM | FAIL (degraded — check fires)
#
# ==============================================================================

# ------------------------------------------------------------------------------
# Shared upstream requirements (ASRs at QM / B / D)
# ------------------------------------------------------------------------------

trlc_requirements(
name = "valid_asr",
srcs = ["valid_asr.trlc"],
spec = ["//bazel/rules/rules_score/trlc/config:score_requirements_model"],
visibility = ["//visibility:private"],
)

# ------------------------------------------------------------------------------
# Valid scenarios — ASIL level is maintained or raised downstream
# bazel test //bazel/rules/rules_score/trlc/config/test:asil_check_valid_test
# Expected result: PASS
# ------------------------------------------------------------------------------

trlc_requirements(
name = "valid_feat_req",
srcs = ["valid_feat_req.trlc"],
spec = ["//bazel/rules/rules_score/trlc/config:score_requirements_model"],
deps = [":valid_asr"],
visibility = ["//visibility:private"],
)

trlc_requirements(
name = "valid_comp_req",
srcs = ["valid_comp_req.trlc"],
spec = ["//bazel/rules/rules_score/trlc/config:score_requirements_model"],
deps = [
":valid_asr",
":valid_feat_req",
],
visibility = ["//visibility:private"],
)

# Only the leaf target is passed — its depset transitively includes valid_feat_req
# and valid_asr (via deps), so the RSL spec file is passed to TRLC exactly once.
trlc_check_test(
name = "asil_check_valid_test",
reqs = [":valid_comp_req"],
)

# ------------------------------------------------------------------------------
# Invalid scenarios — ASIL level is degraded downstream (check violations)
#
# These targets are tagged "manual" because trlc_requirements_test will FAIL
# as expected when TRLC reports the ASIL check violation as an error.
# Run them manually to observe the error output:
#
# bazel test //bazel/rules/rules_score/trlc/config/test:asil_check_invalid_feat_test
# bazel test //bazel/rules/rules_score/trlc/config/test:asil_check_invalid_comp_test
# ------------------------------------------------------------------------------

trlc_requirements(
name = "invalid_feat_req",
srcs = ["invalid_feat_req.trlc"],
spec = ["//bazel/rules/rules_score/trlc/config:score_requirements_model"],
deps = [":valid_asr"],
tags = ["manual"],
visibility = ["//visibility:private"],
)

# Expected result: FAIL — 3 ASIL check violations (FEAT_INVALID_001/002/003)
# invalid_feat_req deps on valid_asr, so all needed files are included transitively.
trlc_check_test(
name = "asil_check_invalid_feat_test",
reqs = [":invalid_feat_req"],
tags = ["manual"],
)

trlc_requirements(
name = "invalid_comp_req",
srcs = ["invalid_comp_req.trlc"],
spec = ["//bazel/rules/rules_score/trlc/config:score_requirements_model"],
deps = [
":valid_asr",
":valid_feat_req",
],
tags = ["manual"],
visibility = ["//visibility:private"],
)

# Expected result: FAIL — 4 ASIL check violations (COMP_INVALID_001/002/003/004)
# invalid_comp_req deps on valid_asr and valid_feat_req, so all needed files are included transitively.
trlc_check_test(
name = "asil_check_invalid_comp_test",
reqs = [":invalid_comp_req"],
tags = ["manual"],
)
59 changes: 59 additions & 0 deletions bazel/rules/rules_score/trlc/config/test/invalid_comp_req.trlc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/********************************************************************************
* 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
********************************************************************************/

// These CompReq entries intentionally violate the ASIL safety check:
// "ASIL level of derived requirement must be at least the same as the upstream requirement"
// Running trlc on these files together with valid_asr.trlc and valid_feat_req.trlc
// MUST produce errors.

package AssilCheckInvalidComp

import ScoreReq
import AssilCheckValid

// INVALID: upstream FeatReq (FEAT_VALID_002) is ASIL B, this CompReq is ASIL QM — degraded.
// Expected error on field: safety
ScoreReq.CompReq COMP_INVALID_001 {
description = "The component shall implement the communication interface — incorrectly declared at ASIL QM despite ASIL B feature requirement."
safety = ScoreReq.Asil.QM
derived_from = [AssilCheckValid.FEAT_VALID_002@1]
version = 1
}

// INVALID: upstream FeatReq (FEAT_VALID_004) is ASIL D, this CompReq is ASIL B — degraded.
// Expected error on field: safety
ScoreReq.CompReq COMP_INVALID_002 {
description = "The component shall implement fault-tolerant operation — incorrectly declared at ASIL B despite ASIL D feature requirement."
safety = ScoreReq.Asil.B
derived_from = [AssilCheckValid.FEAT_VALID_004@1]
version = 1
}

// INVALID: upstream FeatReq (FEAT_VALID_004) is ASIL D, this CompReq is ASIL QM — degraded.
// Expected error on field: safety
ScoreReq.CompReq COMP_INVALID_003 {
description = "The component shall guarantee integrity — incorrectly declared at ASIL QM despite ASIL D feature requirement."
safety = ScoreReq.Asil.QM
derived_from = [AssilCheckValid.FEAT_VALID_004@1]
version = 1
}

// INVALID: upstream ASR (ASR_VALID_003) is ASIL D, this CompReq is ASIL B — degraded
// (direct ASR traceability, bypassing feature level).
// Expected error on field: safety
ScoreReq.CompReq COMP_INVALID_004 {
description = "The component shall handle system-level fault tolerance — incorrectly declared at ASIL B despite ASIL D ASR."
safety = ScoreReq.Asil.B
derived_from = [AssilCheckValid.ASR_VALID_003@1]
version = 1
}
48 changes: 48 additions & 0 deletions bazel/rules/rules_score/trlc/config/test/invalid_feat_req.trlc
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/********************************************************************************
* 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
********************************************************************************/

// These FeatReq entries intentionally violate the ASIL safety check:
// "ASIL level of derived requirement must be at least the same as the upstream requirement"
// Running trlc on these files together with valid_asr.trlc MUST produce errors.

package AssilCheckInvalidFeat

import ScoreReq
import AssilCheckValid

// INVALID: upstream ASR is ASIL B, this FeatReq is ASIL QM — degraded ASIL.
// Expected error on field: safety
ScoreReq.FeatReq FEAT_INVALID_001 {
description = "The component shall report status — incorrectly declared at ASIL QM despite ASIL B upstream."
safety = ScoreReq.Asil.QM
derived_from = [AssilCheckValid.ASR_VALID_002@1]
version = 1
}

// INVALID: upstream ASR is ASIL D, this FeatReq is ASIL B — degraded ASIL.
// Expected error on field: safety
ScoreReq.FeatReq FEAT_INVALID_002 {
description = "The component shall handle fault-tolerant messages — incorrectly declared at ASIL B despite ASIL D upstream."
safety = ScoreReq.Asil.B
derived_from = [AssilCheckValid.ASR_VALID_003@1]
version = 1
}

// INVALID: upstream ASR is ASIL D, this FeatReq is ASIL QM — degraded ASIL.
// Expected error on field: safety
ScoreReq.FeatReq FEAT_INVALID_003 {
description = "The component shall guarantee integrity — incorrectly declared at ASIL QM despite ASIL D upstream."
safety = ScoreReq.Asil.QM
derived_from = [AssilCheckValid.ASR_VALID_003@1]
version = 1
}
45 changes: 45 additions & 0 deletions bazel/rules/rules_score/trlc/config/test/trlc_check_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# *******************************************************************************
# 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
# *******************************************************************************

# trlc_check_test: like trlc_requirements_test but omits --verify.
#
# TRLC 3.0.0's VCG (CVC5 backend) crashes when statically verifying check
# blocks that contain forall over union-typed tuple item fields (e.g.
# CompReqSourceId.item [FeatReq, AssumedSystemReq]). Without --verify TRLC
# still evaluates the checks at runtime against actual requirement instances,
# which is sufficient for functional pass/fail testing. The static VCG
# analysis can be re-enabled once the upstream TRLC bug is fixed.

def trlc_check_test(name, reqs, **kwargs):
"""Run TRLC on requirement files and evaluate user-defined checks.

Unlike the standard trlc_requirements_test rule, this macro does NOT pass
--verify to TRLC, so the CVC5-backed static analysis is skipped. The
checks defined in the RSL model are still evaluated against the TRLC
requirement instances at runtime.

Args:
name: target name
reqs: list of trlc_requirements targets to check
**kwargs: forwarded to native.py_test (e.g. tags, visibility)
"""
native.py_test(
name = name,
srcs = ["@trlc//:trlc.py"],
main = "trlc.py",
# No --verify: skip CVC5/VCG static analysis to avoid the 3.0.0 crash.
args = ["$(locations %s)" % req for req in reqs],
deps = ["@trlc//trlc:trlc"],
data = reqs,
**kwargs
)
41 changes: 41 additions & 0 deletions bazel/rules/rules_score/trlc/config/test/valid_asr.trlc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/********************************************************************************
* 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
********************************************************************************/
package AssilCheckValid

import ScoreReq

// Upstream ASR at QM — used to verify that a QM FeatReq/CompReq is accepted.
ScoreReq.AssumedSystemReq ASR_VALID_001 {
description = "The system shall provide a minimal interface for QM-level functionality."
safety = ScoreReq.Asil.QM
version = 1
rationale = "Baseline QM system requirement."
}

// Upstream ASR at ASIL B — used to verify that ASIL B and ASIL D derived
// requirements are accepted, and that ASIL QM derived requirements are rejected.
ScoreReq.AssumedSystemReq ASR_VALID_002 {
description = "The system shall provide a safe communication channel at ASIL B."
safety = ScoreReq.Asil.B
version = 1
rationale = "ASIL B system requirement for safety-critical communication."
}

// Upstream ASR at ASIL D — used to verify that only ASIL D derived requirements
// are accepted, and that ASIL QM or ASIL B derived requirements are rejected.
ScoreReq.AssumedSystemReq ASR_VALID_003 {
description = "The system shall guarantee fault-tolerant operation at ASIL D."
safety = ScoreReq.Asil.D
version = 1
rationale = "Highest integrity level requirement covering fault-tolerant behaviour."
}
Loading
Loading