use super::*;
#[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 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"));
}
#[test]
fn nth_attribute_present_and_non_null_is_true() {
let entity = b"#40=IFCBUILDINGSTOREY('guid',$,'Level 1',$,$,#18,#39,$,.ELEMENT.,0.);";
assert!(nth_attribute_is_present(entity, 0));
assert!(nth_attribute_is_present(entity, 6));
}
#[test]
fn nth_attribute_dollar_is_false() {
let entity = b"#40=IFCBUILDINGSTOREY('guid',$,'Level 1',$,$,#18,$,$,.ELEMENT.,0.);";
assert!(!nth_attribute_is_present(entity, 6));
assert!(!nth_attribute_is_present(entity, 1));
}
#[test]
fn nth_attribute_past_the_end_is_false() {
let entity = b"#1=IFCWALL('guid',$,'Wall');";
assert!(!nth_attribute_is_present(entity, 10));
}
#[test]
fn nth_attribute_empty_value_is_false() {
let entity = b"#1=IFCFOO('a',,'c');";
assert!(!nth_attribute_is_present(entity, 1));
assert!(nth_attribute_is_present(entity, 0));
assert!(nth_attribute_is_present(entity, 2));
}
#[test]
fn nth_attribute_nested_parens_are_not_top_level_commas() {
let entity = b"#30=IFCPOLYLOOP((#20,#21,#22),$);";
assert!(nth_attribute_is_present(entity, 0));
assert!(!nth_attribute_is_present(entity, 1));
assert!(!nth_attribute_is_present(entity, 2));
}
#[test]
fn nth_attribute_quoted_comma_and_paren_are_not_top_level() {
let entity =
b"#40=IFCBUILDINGSTOREY('guid',$,'Level 1, west (annex)',$);";
assert!(nth_attribute_is_present(entity, 0)); assert!(!nth_attribute_is_present(entity, 1)); assert!(nth_attribute_is_present(entity, 2)); assert!(!nth_attribute_is_present(entity, 3)); assert!(!nth_attribute_is_present(entity, 4));
}
#[test]
fn nth_attribute_escaped_quote_stays_inside_the_string() {
let entity = b"#1=IFCWALL('guid',$,'quo''te',$);";
assert!(nth_attribute_is_present(entity, 2)); assert!(!nth_attribute_is_present(entity, 3)); assert!(!nth_attribute_is_present(entity, 4));
}
#[test]
fn nth_attribute_no_open_paren_is_false() {
assert!(!nth_attribute_is_present(b"#1=IFCWALL;", 0));
}
#[test]
fn nth_attribute_no_close_paren_is_false() {
assert!(!nth_attribute_is_present(b"#1=IFCWALL('guid'", 0));
}
#[test]
fn nth_attribute_reversed_boundary_is_false() {
assert!(!nth_attribute_is_present(b")(", 0));
assert!(!nth_attribute_is_present(b"garbage)stuff(more", 0));
}
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 a_legacy_record_resolves_where_the_decoded_type_cannot() {
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);
assert!(
matches!(decoded, IfcType::Unknown(_)),
"{name} must be Unknown to the bare decoder, or this test proves nothing"
);
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 the_legacy_aware_type_product_gate_widens_by_exactly_three_keywords() {
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(ty)) => assert_eq!(
ty,
IfcType::from_str(kw),
"{kw} was already admitted; its resolved type must not have moved"
),
(true, None) => panic!("{kw}: the legacy-aware gate NARROWED, dropping geometry"),
(false, None) => {}
}
}
widened.sort_by_key(|&(kw, _)| kw);
let mut expected = EXPECTED_NEW.to_vec();
expected.sort_by_key(|&(kw, _)| kw);
assert_eq!(widened, expected, "unexpected widening set");
for (kw, _) in EXPECTED_NEW {
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_legacy_key_is_unknown_to_the_generated_enum() {
for &key in LEGACY_KEYS {
assert!(
matches!(IfcType::from_str(key), IfcType::Unknown(_)),
"{key} is now known to `IfcType::from_str`, so the `Unknown` \
short-circuit in `legacy_aware_ifc_type_from_record` skips its \
legacy remap and the browser diverges from the native path again"
);
}
}