use super::*;
use ifc_lite_core::EntityDecoder;
#[test]
fn parse_first_ref_reads_an_ordinary_ref() {
assert_eq!(parse_first_ref(b"#5=IFCFACETEDBREP(#137924)"), Some(137924));
}
#[test]
fn parse_first_ref_accepts_a_ref_at_exactly_u32_max() {
assert_eq!(
parse_first_ref(b"#5=IFCFACETEDBREP(#4294967295)"),
Some(u32::MAX)
);
}
#[test]
fn parse_first_ref_refuses_a_ref_above_u32_max_instead_of_wrapping_onto_a_real_entity() {
assert_eq!(parse_first_ref(b"#5=IFCFACETEDBREP(#4294967297)"), None);
}
#[test]
fn item_signature_refuses_an_oversized_child_ref_instead_of_aliasing_it_onto_a_real_entity() {
let content = b"#1=IFCCARTESIANPOINT((0.,0.,0.));\n\
#2=IFCEXTRUDEDAREASOLID(#4294967297);\n\
#3=IFCEXTRUDEDAREASOLID(#999999999);\n\
#4=IFCEXTRUDEDAREASOLID(#1);\n";
let mut decoder = EntityDecoder::new(content);
let mut memo = FxHashMap::default();
let mut refused = 0usize;
let oversized_ref_sig = item_signature(&mut decoder, 2, &mut memo, &mut refused);
let mut decoder = EntityDecoder::new(content);
let mut memo = FxHashMap::default();
let mut refused2 = 0usize;
let missing_ref_sig = item_signature(&mut decoder, 3, &mut memo, &mut refused2);
let mut decoder = EntityDecoder::new(content);
let mut memo = FxHashMap::default();
let mut refused3 = 0usize;
let real_ref_sig = item_signature(&mut decoder, 4, &mut memo, &mut refused3);
assert_eq!(
oversized_ref_sig, missing_ref_sig,
"an oversized child ref must hash exactly like a genuinely missing ref"
);
assert_ne!(
oversized_ref_sig, real_ref_sig,
"an oversized child ref must NEVER hash like the real entity #1 it would wrap onto"
);
}
#[test]
fn item_signature_counts_an_oversized_child_ref_as_refused() {
let content = b"#1=IFCCARTESIANPOINT((0.,0.,0.));\n\
#2=IFCEXTRUDEDAREASOLID(#4294967297);\n";
let mut decoder = EntityDecoder::new(content);
let mut memo = FxHashMap::default();
let mut refused = 0usize;
item_signature(&mut decoder, 2, &mut memo, &mut refused);
assert_eq!(
refused, 1,
"an oversized child ref must be counted, not silently dropped (#3752)"
);
}
#[test]
fn item_signature_reports_no_refusals_for_an_ordinary_ref() {
let content = b"#1=IFCCARTESIANPOINT((0.,0.,0.));\n\
#2=IFCEXTRUDEDAREASOLID(#1);\n";
let mut decoder = EntityDecoder::new(content);
let mut memo = FxHashMap::default();
let mut refused = 0usize;
item_signature(&mut decoder, 2, &mut memo, &mut refused);
assert_eq!(refused, 0, "an ordinary reference must not be counted as refused");
}
#[test]
fn item_signature_reports_no_refusals_for_a_ref_at_exactly_u32_max() {
let content = b"#4294967295=IFCCARTESIANPOINT((0.,0.,0.));\n\
#2=IFCEXTRUDEDAREASOLID(#4294967295);\n";
let mut decoder = EntityDecoder::new(content);
let mut memo = FxHashMap::default();
let mut refused = 0usize;
item_signature(&mut decoder, 2, &mut memo, &mut refused);
assert_eq!(refused, 0, "a ref at exactly u32::MAX must not be counted as refused");
}
#[test]
fn geometry_routing_key_feeds_take_content_hash_oversized_ref_drops() {
use ifc_lite_core::{AttributeValue, DecodedEntity, IfcType};
let content = b"#2=IFCEXTRUDEDAREASOLID(#4294967297);\n";
let mut decoder = EntityDecoder::new(content);
let element = DecodedEntity::new(
1,
IfcType::IfcWall,
vec![
AttributeValue::Null,
AttributeValue::Null,
AttributeValue::Null,
AttributeValue::Null,
AttributeValue::Null,
AttributeValue::Null,
AttributeValue::EntityRef(2), ],
);
let router = crate::GeometryRouter::new();
let key = router.geometry_routing_key(&element, &mut decoder);
assert!(key.is_some(), "an element with a representation must still get a routing key");
assert_eq!(
router.take_content_hash_oversized_ref_drops(),
1,
"the oversized child ref hit while routing must be counted (#3752)"
);
assert_eq!(
router.take_content_hash_oversized_ref_drops(),
0,
"a second drain must return zero — the counter resets, it isn't recomputed"
);
}
#[test]
fn issue_3988_brep_signature_reuses_scratch_without_stale_faces_or_points() {
let source = "\
#1=IFCFACETEDBREP(#2);
#2=IFCCLOSEDSHELL((#3,#4));
#3=IFCFACE((#5,#6));
#4=IFCFACE((#7));
#5=IFCFACEOUTERBOUND(#10,.T.);
#6=IFCFACEBOUND(#11,.F.);
#7=IFCFACEOUTERBOUND(#12,.T.);
#10=IFCPOLYLOOP((#20,#21,#22,#23));
#11=IFCPOLYLOOP((#20,#21,#22));
#12=IFCPOLYLOOP((#21,#22,#23));
#20=IFCCARTESIANPOINT((0.,0.,0.));
#21=IFCCARTESIANPOINT((1.,0.,0.));
#22=IFCCARTESIANPOINT((0.,1.,0.));
#23=IFCCARTESIANPOINT((0.,0.,1.));
";
let mut decoder = EntityDecoder::new(source);
let signature = try_faceted_brep_signature(&mut decoder, 1).expect("complete BREP");
assert_eq!(decoder.point_cache_stats(), (6, 4), "ten authored corners, four unique points");
let mut renumbered = source.to_string();
for id in (1..=23).rev() {
for suffix in [",", ")", "="] {
renumbered = renumbered.replace(&format!("#{id}{suffix}"), &format!("#{}{suffix}", id + 100));
}
}
let mut other = EntityDecoder::new(&renumbered);
assert_eq!(try_faceted_brep_signature(&mut other, 101), Some(signature));
let missing = source.replace("#12=IFCPOLYLOOP((#21,#22,#23))", "#12=IFCPOLYLOOP((#21,#22,#99))");
let mut missing_decoder = EntityDecoder::new(&missing);
assert_eq!(try_faceted_brep_signature(&mut missing_decoder, 1), None);
}