use super::common::*;
use sysml_v2_parser::ast::*;
use sysml_v2_parser::{parse, parse_with_diagnostics};
#[test]
fn test_package_with_semicolon_body() {
let input = "package Foo;";
let result = parse(input).expect("parse should succeed");
let expected = expected_package_foo_semicolon();
assert_eq!(
result, expected,
"AST should match expected for package Foo;"
);
}
#[test]
fn test_package_with_brace_body() {
let input = "package Bar { }";
let result = parse(input).expect("parse should succeed");
let expected = expected_package_bar_brace();
assert_eq!(
result, expected,
"AST should match expected for package Bar {{ }}"
);
}
#[test]
fn test_standard_library_package_header_parses() {
let input = "standard library package SysML { }";
let result = parse(input).expect("parse should succeed");
assert_eq!(result.elements.len(), 1);
match &result.elements[0].value {
RootElement::LibraryPackage(lp) => {
assert!(lp.value.is_standard);
assert_eq!(lp.value.identification.name.as_deref(), Some("SysML"));
assert!(
matches!(lp.value.body, PackageBody::Brace { ref elements } if elements.is_empty())
);
}
other => panic!("expected library package, got {:?}", other),
}
}
#[test]
fn test_legacy_library_standard_package_header_still_parses() {
let input = "library standard package LegacyStd;";
let result = parse(input).expect("parse should succeed");
assert_eq!(
result,
RootNamespace {
elements: vec![n_len(
input.len(),
RootElement::LibraryPackage(n_len(
input.len(),
LibraryPackage {
is_standard: true,
identification: id("LegacyStd"),
body: PackageBody::Semicolon,
}
))
)]
}
);
}
#[test]
fn test_library_abstract_action_feature_decl_parses_without_diagnostics() {
let input = r#"package P {
abstract action sendActions: SendAction[0..*] nonunique :> actions, sendPerformances {
doc /* sendActions is the base feature for SendActionUsages. */
}
}"#;
let result = parse_with_diagnostics(input);
assert!(
result.errors.is_empty(),
"expected no diagnostics; got: {:?}",
result.errors
);
}
#[test]
fn test_library_multiplicity_decl_parses_without_diagnostics() {
let input = r#"package P {
multiplicity exactlyOne [1..1] { doc /* ... */ }
}"#;
let result = parse_with_diagnostics(input);
assert!(
result.errors.is_empty(),
"expected no diagnostics; got: {:?}",
result.errors
);
}
#[test]
fn test_library_interaction_decl_parses_without_diagnostics() {
let input = r#"package P {
interaction Transfer specializes Performance { doc /* ... */ }
}"#;
let result = parse_with_diagnostics(input);
assert!(
result.errors.is_empty(),
"expected no diagnostics; got: {:?}",
result.errors
);
}
#[test]
fn test_library_return_assignment_form_parses_without_diagnostics() {
let input = r#"package P {
calc def C {
return result = integrate.result;
}
}"#;
let result = parse_with_diagnostics(input);
assert!(
result.errors.is_empty(),
"expected no diagnostics; got: {:?}",
result.errors
);
}
#[test]
fn test_root_level_import_then_package() {
let input = "private import Views::*;\npackage P { }";
let result = parse(input).expect("parse should succeed");
assert_eq!(result.elements.len(), 2);
match &result.elements[0].value {
sysml_v2_parser::ast::RootElement::Import(_) => {}
_ => panic!("expected first element to be Import"),
}
match &result.elements[1].value {
sysml_v2_parser::ast::RootElement::Package(p) => {
assert_eq!(p.identification.name.as_deref(), Some("P"));
}
_ => panic!("expected second element to be Package"),
}
}
#[test]
fn test_stdlib_requirement_usecase_enum_map_to_dedicated_nodes() {
let input = "package P {
abstract requirement def RequirementCheck :> BaseType { }
use case def UseCase :> Case { }
enum def VerdictKind { pass; fail; }
}";
let result = parse(input).expect("parse should succeed");
let pkg = match &result.elements[0].value {
RootElement::Package(p) => &p.value,
_ => panic!("expected package"),
};
let elements = match &pkg.body {
PackageBody::Brace { elements } => elements,
_ => panic!("expected brace body"),
};
assert!(matches!(
elements[0].value,
PackageBodyElement::RequirementDef(_)
));
let PackageBodyElement::RequirementDef(req) = &elements[0].value else {
panic!("expected requirement def");
};
assert_eq!(req.value.specializes.as_deref(), Some("BaseType"));
assert!(matches!(
elements[1].value,
PackageBodyElement::UseCaseDef(_)
));
let PackageBodyElement::UseCaseDef(uc) = &elements[1].value else {
panic!("expected use case def");
};
assert_eq!(uc.value.specializes.as_deref(), Some("Case"));
assert!(matches!(elements[2].value, PackageBodyElement::EnumDef(_)));
}
#[test]
fn test_stdlib_part_port_viewpoint_map_to_dedicated_nodes() {
let input = "package P {
abstract part def Part :> Item { }
abstract port def Port :> Object { }
abstract viewpoint def ViewpointCheck :> RequirementCheck { }
}";
let result = parse(input).expect("parse should succeed");
let pkg = match &result.elements[0].value {
RootElement::Package(p) => &p.value,
_ => panic!("expected package"),
};
let elements = match &pkg.body {
PackageBody::Brace { elements } => elements,
_ => panic!("expected brace body"),
};
assert!(matches!(elements[0].value, PackageBodyElement::PartDef(_)));
let PackageBodyElement::PartDef(part) = &elements[0].value else {
panic!("expected part def");
};
assert_eq!(part.value.specializes.as_deref(), Some("Item"));
assert!(matches!(elements[1].value, PackageBodyElement::PortDef(_)));
let PackageBodyElement::PortDef(port) = &elements[1].value else {
panic!("expected port def");
};
assert_eq!(port.value.specializes.as_deref(), Some("Object"));
assert!(matches!(
elements[2].value,
PackageBodyElement::ViewpointDef(_)
));
let PackageBodyElement::ViewpointDef(vp) = &elements[2].value else {
panic!("expected viewpoint def");
};
assert_eq!(vp.value.specializes.as_deref(), Some("RequirementCheck"));
assert!(
!elements
.iter()
.any(|e| matches!(e.value, PackageBodyElement::ExtendedLibraryDecl(_))),
"sample should not fall back to ExtendedLibraryDecl"
);
}
#[test]
fn test_feature_and_classifier_decls_map_to_dedicated_package_nodes() {
let input = "package P {
feature myFeature : BaseFeature;
class VehicleClass;
struct LayoutStruct;
}";
let result = parse(input).expect("parse should succeed");
let pkg = match &result.elements[0].value {
RootElement::Package(p) => &p.value,
_ => panic!("expected package"),
};
let elements = match &pkg.body {
PackageBody::Brace { elements } => elements,
_ => panic!("expected brace body"),
};
assert!(matches!(
elements[0].value,
PackageBodyElement::FeatureDecl(_)
));
assert!(matches!(
elements[1].value,
PackageBodyElement::ClassifierDecl(_)
));
assert!(matches!(
elements[2].value,
PackageBodyElement::ClassifierDecl(_)
));
assert!(
!elements.iter().any(|e| matches!(
e.value,
PackageBodyElement::KermlSemanticDecl(_) | PackageBodyElement::KermlFeatureDecl(_)
)),
"dedicated feature/classifier samples should not fall back to generic KerML buckets"
);
}
#[test]
fn test_kerml_fallback_family_keywords_map_to_dedicated_nodes() {
let input = r#"package P {
structure PhysicalStructure;
behavior B;
function F;
interaction I;
datatype D;
association A;
metaclass M;
step S;
invariant Inv;
predicate P;
}"#;
let result = parse(input).expect("parse should succeed");
let pkg = match &result.elements[0].value {
RootElement::Package(p) => &p.value,
_ => panic!("expected package"),
};
let elements = match &pkg.body {
PackageBody::Brace { elements } => elements,
_ => panic!("expected brace body"),
};
assert!(matches!(
elements[0].value,
PackageBodyElement::ClassifierDecl(_)
));
for (idx, element) in elements.iter().enumerate().take(9).skip(1) {
assert!(
matches!(element.value, PackageBodyElement::KermlSemanticDecl(_)),
"expected KermlSemanticDecl at index {idx}, got {:?}",
element.value
);
}
assert!(matches!(
elements[9].value,
PackageBodyElement::KermlFeatureDecl(_)
));
assert!(
!elements
.iter()
.any(|e| matches!(e.value, PackageBodyElement::ExtendedLibraryDecl(_))),
"samples should not fall back to ExtendedLibraryDecl"
);
}
#[test]
fn test_quantities_abstract_attribute_def_maps_dedicated() {
let input = "package P { abstract attribute def TensorQuantityValue :> Array { attribute num: Number[1..*]; } }";
let result = parse(input).expect("parse should succeed");
let pkg = match &result.elements[0].value {
RootElement::Package(p) => &p.value,
_ => panic!("expected package"),
};
let elements = match &pkg.body {
PackageBody::Brace { elements } => elements,
_ => panic!("expected brace body"),
};
assert!(matches!(
elements[0].value,
PackageBodyElement::AttributeDef(_)
));
}
#[test]
fn test_comment_about_member_does_not_consume_next_package() {
let input = r#"package P {
part def BMS {
}
comment about BMS
/* BMS = Battery Management System */
}
package Next {
part def BatteryLevelComputer;
}"#;
let result = parse_with_diagnostics(input);
assert!(
result.errors.is_empty(),
"comment about should parse without package-boundary recovery: {:?}",
result.errors
);
assert_eq!(result.root.elements.len(), 2);
}
#[test]
fn test_parse_package_with_quoted_name() {
let input = "package '15.10-Primitive Data Types' { }";
let result = parse(input).expect("quoted package names should parse");
let pkg = match &result.elements[0].value {
RootElement::Package(p) => &p.value,
_ => panic!("expected package"),
};
assert_eq!(
pkg.identification.name.as_deref(),
Some("15.10-Primitive Data Types")
);
}
#[test]
fn test_qualified_package_declaration_parses() {
let input = "package AstronomyReference::Domain { part def Thing; }";
let result = sysml_v2_parser::parse_with_diagnostics(input);
assert!(
result.errors.is_empty(),
"qualified package declaration should parse cleanly: {:?}",
result.errors
);
let package = match &result.root.elements[0].value {
RootElement::Package(package) => &package.value,
other => panic!("expected package root element, got {other:?}"),
};
assert_eq!(
package.identification.name.as_deref(),
Some("AstronomyReference::Domain")
);
}