use super::*;
#[test]
fn immutable_classification_matches_predicates_for_entire_schema_and_legacy_catalog() {
for name in crate::generated::IFC_TYPES.iter().map(IfcType::as_str)
.chain(crate::legacy_entities::LEGACY_ENTITY_NAMES.iter().copied())
{
let expected = (
compute_has_geometry(name),
compute_is_representationless_spatial_container(name),
compute_is_simple(name),
);
let mixed: String = name.chars().enumerate().map(|(i, ch)| {
if i % 2 == 0 { ch.to_ascii_lowercase() } else { ch }
}).collect();
for spelling in [name.to_owned(), name.to_ascii_lowercase(), mixed] {
assert_eq!(
(
has_geometry_by_name(&spelling),
is_representationless_spatial_container_by_name(&spelling),
is_simple_geometry_type(&spelling),
),
expected,
"classification changed for {spelling}",
);
}
}
}
#[test]
fn unknown_classification_preserves_fallback_without_retaining_names() {
let table_len = classifications().len();
for (name, geometry) in [
("IfcReinforcingVendorExtension", true),
("IfcReinforcedVendorExtension", true),
("IfcVendorReinforcingExtension", false),
("IfcVendorGeometry", false),
("", false),
("IfcVéndor", false),
] {
assert_eq!(has_geometry_by_name(name), geometry, "{name}");
assert!(!is_representationless_spatial_container_by_name(name), "{name}");
assert!(is_simple_geometry_type(name), "{name}");
assert!(!classifications().contains_key(name.to_ascii_uppercase().as_str()));
}
assert_eq!(classifications().len(), table_len);
}
#[test]
fn building_elements_have_geometry() {
for name in [
"IFCWALL",
"IFCSLAB",
"IFCBEAM",
"IFCCOLUMN",
"IFCDOOR",
"IFCWINDOW",
"IFCROOF",
"IFCSTAIR",
"IFCSHADINGDEVICE",
] {
assert!(has_geometry_by_name(name), "{name} should have geometry");
}
}
#[test]
fn mep_elements_have_geometry() {
for name in [
"IFCFLOWSEGMENT",
"IFCFLOWFITTING",
"IFCENERGYCONVERSIONDEVICE",
"IFCFLOWTREATMENTDEVICE",
"IFCBOILER",
"IFCPUMP",
"IFCVALVE",
] {
assert!(has_geometry_by_name(name), "{name} should have geometry");
}
}
#[test]
fn solar_device_has_geometry() {
assert!(has_geometry_by_name("IFCSOLARDEVICE"));
assert!(has_geometry_by_name("IfcSolarDevice"));
}
#[test]
fn distribution_leaves_the_deleted_whitelist_missed_have_geometry() {
for name in ["IFCCHILLER", "IFCCOIL", "IFCFAN", "IFCLIGHTFIXTURE", "IFCSENSOR"] {
assert!(has_geometry_by_name(name), "{name}");
}
assert!(!has_geometry_by_name("IfcReinforcementBarProperties"));
}
#[test]
fn ifc4x3_infrastructure_have_geometry() {
for name in [
"IFCBEARING",
"IFCKERB",
"IFCPAVEMENT",
"IFCRAIL",
"IFCTRACKELEMENT",
"IFCSIGN",
"IFCSIGNAL",
"IFCEARTHWORKSCUT",
] {
assert!(has_geometry_by_name(name), "{name} should have geometry");
}
}
#[test]
fn reinforcement_variants_have_geometry() {
assert!(has_geometry_by_name("IFCREINFORCINGBAR"));
assert!(has_geometry_by_name("IFCREINFORCINGMESH"));
assert!(has_geometry_by_name("IFCREINFORCEDSOIL"));
}
#[test]
fn standardcase_and_elementedcase_have_geometry() {
for name in [
"IFCBEAMSTANDARDCASE",
"IFCSLABSTANDARDCASE",
"IFCSLABELEMENTEDCASE",
"IFCWALLSTANDARDCASE",
"IFCWALLELEMENTEDCASE",
"IFCDOORSTANDARDCASE",
"IFCWINDOWSTANDARDCASE",
"IFCOPENINGSTANDARDCASE",
] {
assert!(has_geometry_by_name(name), "{name} should have geometry");
}
}
#[test]
fn space_and_site_have_geometry() {
assert!(has_geometry_by_name("IFCSPACE"));
assert!(has_geometry_by_name("IFCSITE"));
assert!(has_geometry_by_name("IFCOPENINGELEMENT"));
assert!(has_geometry_by_name("IFCSPATIALZONE"));
}
#[test]
fn building_bears_geometry() {
assert!(has_geometry_by_name("IFCBUILDING"));
assert!(has_geometry_by_name("IfcBuilding"));
assert!(!has_geometry_by_name("IFCBUILDINGSTOREY"));
assert!(!has_geometry_by_name("IFCFACILITY"));
}
#[test]
fn legacy_ifc2x3_distribution_names_have_geometry() {
assert!(has_geometry_by_name("IFCEQUIPMENTELEMENT"));
assert!(has_geometry_by_name("IFCELECTRICDISTRIBUTIONPOINT"));
assert!(!has_geometry_by_name("IFCELECTRICALDISTRIBUTIONPOINT"));
}
#[test]
fn non_geometric_spatial_excluded() {
for name in [
"IFCBUILDINGSTOREY",
"IFCFACILITY",
"IFCFACILITYPART",
"IFCSPATIALELEMENT",
"IFCSPATIALSTRUCTUREELEMENT",
"IFCBRIDGE",
"IFCROAD",
"IFCRAILWAY",
"IFCMARINEFACILITY",
"IFCBRIDGEPART",
"IFCFACILITYPARTCOMMON",
"IFCEXTERNALSPATIALELEMENT",
"IFCEXTERNALSPATIALSTRUCTUREELEMENT",
] {
assert!(!has_geometry_by_name(name), "{name} should NOT have geometry");
}
}
#[test]
fn non_products_excluded() {
for name in [
"IFCPROJECT",
"IFCMATERIAL",
"IFCPROPERTYSET",
"IFCRELAGGREGATES",
"IFCDIMENSIONALEXPONENTS",
"IFCSURFACESTYLERENDERING",
"IFCGEOMETRICREPRESENTATIONSUBCONTEXT",
"IFCCARTESIANPOINT",
] {
assert!(!has_geometry_by_name(name), "{name} should NOT have geometry");
}
}
#[test]
fn legacy_proxy_and_buildingelement_have_geometry() {
assert!(has_geometry_by_name("IFCPROXY"));
assert!(has_geometry_by_name("IFCBUILDINGELEMENT"));
}
#[test]
fn unknown_garbage_excluded() {
assert!(!has_geometry_by_name("IFCNOTAREALTYPE"));
assert!(!has_geometry_by_name(""));
assert!(!has_geometry_by_name("FOOREINFORCEDBAR"));
}
#[test]
fn cached_results_are_consistent() {
for _ in 0..3 {
assert!(has_geometry_by_name("IFCWALL"));
assert!(!has_geometry_by_name("IFCPROJECT"));
assert!(is_simple_geometry_type("IFCWALL"));
assert!(!is_simple_geometry_type("IFCWINDOW"));
}
}
#[test]
fn is_simple_geometry_type_routes_correctly() {
assert!(is_simple_geometry_type("IFCWALL"));
assert!(is_simple_geometry_type("IFCSLAB"));
assert!(is_simple_geometry_type("IFCBEAM"));
assert!(is_simple_geometry_type("IFCCOLUMN"));
assert!(!is_simple_geometry_type("IFCWINDOW"));
assert!(!is_simple_geometry_type("IFCDOOR"));
assert!(!is_simple_geometry_type("IFCOPENINGELEMENT"));
assert!(!is_simple_geometry_type("IFCFLOWSEGMENT"));
assert!(!is_simple_geometry_type("IFCSOLARDEVICE"));
assert!(!is_simple_geometry_type("IFCSPACE"));
assert!(!is_simple_geometry_type("IFCANNOTATION"));
assert!(!is_simple_geometry_type("IFCBUILDINGELEMENTPROXY"));
assert!(is_simple_geometry_type("IfcWall"));
assert!(!is_simple_geometry_type("IfcDoor"));
}
const LEGACY_KEYS: &[&str] = crate::legacy_entities::LEGACY_ENTITY_NAMES;
#[test]
fn the_legacy_key_list_is_not_empty() {
assert!(
LEGACY_KEYS.len() >= 20,
"LEGACY_KEYS came back with {} names; the two loops that read it would pass vacuously",
LEGACY_KEYS.len()
);
}
#[test]
fn legacy_ifc2x3_products_resolve_to_their_own_supertype() {
for (name, expected) in [
("IFCELECTRICALELEMENT", IfcType::IfcElement),
("IFCELECTRICDISTRIBUTIONPOINT", IfcType::IfcFlowController),
("IFCCHAMFEREDGEFEATURE", IfcType::IfcFeatureElementSubtraction),
("IFCROUNDEDEDGEFEATURE", IfcType::IfcFeatureElementSubtraction),
(
"IFCSTRUCTURALLINEARACTIONVARYING",
IfcType::IfcStructuralLinearAction,
),
(
"IFCSTRUCTURALPLANARACTIONVARYING",
IfcType::IfcStructuralPlanarAction,
),
] {
let resolved = legacy_aware_ifc_type(name);
assert_eq!(resolved, expected, "{name} resolved to {resolved:?}");
assert!(
resolved.is_subtype_of(IfcType::IfcProduct),
"{name} must reach IfcProduct or the attribute exporter drops it"
);
assert!(
has_geometry_by_name(name),
"{name} must reach the geometry pass"
);
}
}
#[test]
fn the_electric_distribution_point_remap_only_narrows() {
let t = legacy_aware_ifc_type("IFCELECTRICDISTRIBUTIONPOINT");
assert_eq!(t, IfcType::IfcFlowController);
assert!(
t.is_subtype_of(IfcType::IfcDistributionElement),
"the arm used to answer IfcDistributionElement; IfcFlowController must still be one"
);
}
#[test]
fn edge_features_are_subtraction_operands_not_openings() {
for name in ["IFCCHAMFEREDGEFEATURE", "IFCROUNDEDEDGEFEATURE"] {
let t = legacy_aware_ifc_type(name);
assert!(t.is_subtype_of(IfcType::IfcFeatureElement), "{name}");
assert_ne!(t, IfcType::IfcOpeningElement, "{name}");
assert!(!t.is_subtype_of(IfcType::IfcOpeningElement), "{name}");
}
}
#[test]
fn every_legacy_arm_maps_onto_a_known_product() {
for &name in LEGACY_KEYS {
let info = crate::legacy_entities::get_legacy_entity_info(name)
.unwrap_or_else(|| panic!("{name} has no arm in legacy_entities.rs"));
assert!(
!matches!(info.base_type, IfcType::Unknown(_)),
"{name} maps to Unknown, which is the same as not being in the table"
);
if info.has_geometry {
assert!(
info.base_type.is_subtype_of(IfcType::IfcProduct),
"{name} claims geometry but its base type is not an IfcProduct"
);
}
}
}
#[test]
fn an_exact_legacy_variant_keeps_the_existing_processing_mapping() {
for (record, expected) in [
(
&b"#12=IFCPROXY('guid',$,$,$,$,$,$,$,$);"[..],
IfcType::IfcBuildingElementProxy,
),
(
&b"#13=IFCSLABSTANDARDCASE('guid',$,$,$,$,$,$,$,$);"[..],
IfcType::IfcSlab,
),
(
&b"#14=IFCDOORSTANDARDCASE('guid',$,$,$,$,$,$,$,$);"[..],
IfcType::IfcDoor,
),
(
&b"#15=IFCSOLIDSTRATUM('guid',$,$,$,$,$,$,$,$);"[..],
IfcType::IfcGeotechnicalStratum,
),
(
&b"#16= IFCPROXY('guid',$,$,$,$,$,$,$,$);"[..],
IfcType::IfcBuildingElementProxy,
),
(
&b"#17 = IFCSLABSTANDARDCASE('guid',$,$);"[..],
IfcType::IfcSlab,
),
(
&b"#18=\tIFCDOORSTANDARDCASE('guid',$,$);"[..],
IfcType::IfcDoor,
),
] {
let name = crate::fast_parse::extract_entity_type_name(record).unwrap();
let decoded = IfcType::from_str(name);
if matches!(
name,
"IFCSOLIDSTRATUM" | "IFCVOIDSTRATUM" | "IFCWATERSTRATUM"
) {
assert!(matches!(decoded, IfcType::Unknown(_)), "{name}");
} else {
assert_eq!(decoded.as_str(), name, "{name} must retain its exact name");
}
assert_eq!(
legacy_aware_ifc_type_from_record(decoded, record),
expected,
"{name}"
);
}
}
#[test]
fn a_known_type_is_returned_untouched_whatever_the_record_says() {
let wall = IfcType::from_str("IFCWALL");
assert!(!matches!(wall, IfcType::Unknown(_)));
assert_eq!(
legacy_aware_ifc_type_from_record(wall, b"#1=IFCSLABSTANDARDCASE('g');"),
IfcType::IfcWall
);
}
#[test]
fn an_unreadable_record_changes_nothing() {
let unknown = IfcType::from_str("IFCNOTAREALENTITY");
assert!(matches!(unknown, IfcType::Unknown(_)));
for record in [&b""[..], &b"garbage with no equals or paren"[..], &b"#1="[..], &b"#1=("[..]] {
assert_eq!(legacy_aware_ifc_type_from_record(unknown, record), unknown);
}
}
#[test]
fn exact_legacy_type_products_keep_the_existing_processing_mapping() {
const EXPECTED_NEW: [(&str, IfcType); 3] = [
("IFCDOORSTYLE", IfcType::IfcDoorType),
("IFCWINDOWSTYLE", IfcType::IfcWindowType),
("IFCBUILDINGELEMENTTYPE", IfcType::IfcBuiltElementType),
];
fn bare_gate(kw: &str) -> bool {
(kw.ends_with("TYPE") || kw.ends_with("STYLE"))
&& IfcType::from_str(kw).is_subtype_of(IfcType::IfcTypeProduct)
}
let legacy = crate::LEGACY_ENTITY_NAMES.iter().copied();
let mut widened: Vec<(&str, IfcType)> = Vec::new();
let names: Vec<String> = crate::IFC_TYPES
.iter()
.map(|t: &IfcType| t.name().to_uppercase())
.collect();
for kw in names.iter().map(String::as_str).chain(legacy) {
match (bare_gate(kw), type_product_ifc_type(kw)) {
(false, Some(ty)) => widened.push((kw, ty)),
(true, Some(_)) => {}
(true, None) => panic!("{kw}: the legacy-aware gate NARROWED, dropping geometry"),
(false, None) => {}
}
}
widened.sort_by_key(|&(kw, _)| kw);
assert!(
widened.is_empty(),
"the exact type universe should need no widening: {widened:?}"
);
for (kw, expected) in EXPECTED_NEW {
assert!(
bare_gate(kw),
"{kw} must be admitted by its exact generated variant"
);
assert_eq!(type_product_ifc_type(kw), Some(expected), "{kw}");
assert!(!has_geometry_by_name(kw), "{kw} would render twice");
assert!(
!legacy_aware_ifc_type(kw).is_subtype_of(IfcType::IfcProduct),
"{kw} would get both a product row and a type-product row"
);
}
}
#[test]
fn every_supported_legacy_key_preserves_its_name_or_is_an_explicit_extension() {
for &key in LEGACY_KEYS {
let parsed = IfcType::from_str(key);
if matches!(
key,
"IFCSOLIDSTRATUM" | "IFCVOIDSTRATUM" | "IFCWATERSTRATUM"
) {
assert!(matches!(parsed, IfcType::Unknown(_)), "{key}");
} else {
assert_eq!(parsed.as_str(), key, "{key}");
}
let record = format!("#1={key}();");
assert_eq!(
legacy_aware_ifc_type_from_record(parsed, record.as_bytes()),
legacy_aware_ifc_type(key),
"{key}"
);
}
}
#[test]
fn combined_geometry_flags_match_canonical_predicates_3987() {
let table_len = classifications().len();
for name in crate::generated::IFC_TYPES.iter().map(IfcType::as_str)
.chain(crate::legacy_entities::LEGACY_ENTITY_NAMES.iter().copied())
.chain([
"", "IfcVendorGeometry", "IfcReinforcingVendorExtension",
"IfcReinforcedVendorExtension", "IfcVendorReinforcingExtension",
"IfcVéndor", "IFCſPACE", "IfcßBuilding", "ifcwall ",
])
{
let mixed: String = name.chars().enumerate().map(|(i, ch)| {
if i % 2 == 0 { ch.to_ascii_lowercase() } else { ch }
}).collect();
for spelling in [name.to_owned(), name.to_ascii_lowercase(), mixed] {
let upper = normalise_uppercase(&spelling);
let expected = (
compute_has_geometry(upper.as_ref()),
compute_is_representationless_spatial_container(upper.as_ref()),
);
assert_eq!(geometry_flags_by_name(&spelling), expected, "{spelling}");
assert_eq!(
geometry_flags_by_name(&spelling),
(has_geometry_by_name(&spelling),
is_representationless_spatial_container_by_name(&spelling)),
"separate public predicates differ for {spelling}",
);
}
}
assert_eq!(classifications().len(), table_len);
}