use ifc_lite_core::{build_entity_index, EntityDecoder, EntityScanner};
use ifc_lite_geometry::{GeometryRouter, Mesh};
const FIXTURES: &[&str] = &[
"tests/models/buildingsmart/annex_e/mapped-geometric-shape/mapped-shape-with-transformation.ifc",
"tests/models/buildingsmart/annex_e/mapped-geometric-shape/mapped-shape-without-transformation.ifc",
"tests/models/buildingsmart/annex_e/mapped-geometric-shape/mapped-shape-with-multiple-items.ifc",
];
fn repo_root() -> Option<std::path::PathBuf> {
let mut dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf();
loop {
if dir.join("rust").is_dir() && dir.join("apps").is_dir() {
return Some(dir);
}
if !dir.pop() {
return None;
}
}
}
#[derive(PartialEq, Debug)]
struct MeshFingerprint {
positions: Vec<u32>,
normals: Vec<u32>,
indices: Vec<u32>,
rep_identity: Option<u128>,
local_transform: Option<Vec<u64>>,
}
fn fingerprint(mesh: &Mesh) -> MeshFingerprint {
MeshFingerprint {
positions: mesh.positions.iter().map(|f| f.to_bits()).collect(),
normals: mesh.normals.iter().map(|f| f.to_bits()).collect(),
indices: mesh.indices.clone(),
rep_identity: mesh.instance_meta.as_ref().map(|m| m.rep_identity),
local_transform: mesh
.instance_meta
.as_ref()
.and_then(|m| m.local_transform.as_ref())
.map(|t| t.iter().map(|f| f.to_bits()).collect()),
}
}
fn geometry_product_ids(content: &str, router: &GeometryRouter, decoder: &mut EntityDecoder) -> Vec<u32> {
let mut ids = Vec::new();
let mut scanner = EntityScanner::new(content);
while let Some((id, _name, _, _)) = scanner.next_entity() {
let Ok(entity) = decoder.decode_by_id(id) else {
continue;
};
if let Ok(mesh) = router.process_element(&entity, decoder) {
if !mesh.positions.is_empty() {
ids.push(id);
}
}
}
ids
}
fn fresh_router_with_shared<'a>(
content: &'a str,
shared: &ifc_lite_geometry::SharedMappedItemCache,
) -> (GeometryRouter, EntityDecoder<'a>) {
let mut decoder = EntityDecoder::with_index(content, build_entity_index(content));
let mut router = GeometryRouter::with_units(content, &mut decoder);
router.enable_shared_mapped_item_cache(shared.clone());
(router, decoder)
}
fn assert_cross_router_hit_is_byte_identical(rel_path: &str) {
let Some(root) = repo_root() else {
eprintln!("repo root not found (packaged context) - skipping {rel_path}");
return;
};
let path = root.join(rel_path);
let Ok(content) = std::fs::read_to_string(&path) else {
eprintln!("skipping: fixture {rel_path} not present - run `pnpm fixtures`");
return;
};
let mut base_decoder = EntityDecoder::with_index(&content, build_entity_index(&content));
let base_router = GeometryRouter::with_units(&content, &mut base_decoder);
let product_ids = geometry_product_ids(&content, &base_router, &mut base_decoder);
assert!(
!product_ids.is_empty(),
"fixture {rel_path} produced no geometry - it may no longer exercise the mapped path"
);
let baseline: Vec<MeshFingerprint> = product_ids
.iter()
.map(|id| {
let entity = base_decoder.decode_by_id(*id).expect("decode baseline product");
let mesh = base_router
.process_element(&entity, &mut base_decoder)
.expect("mesh baseline product");
fingerprint(&mesh)
})
.collect();
let shared = GeometryRouter::new_mapped_item_cache();
{
let (warm_router, mut warm_decoder) = fresh_router_with_shared(&content, &shared);
for id in &product_ids {
let entity = warm_decoder.decode_by_id(*id).expect("decode warm product");
let _ = warm_router
.process_element(&entity, &mut warm_decoder)
.expect("mesh warm product");
}
assert!(
warm_router.mapped_shared_unique_count() > 0,
"shared mapped cache stayed empty for {rel_path} - the shared cache was not exercised, \
so the hit path below would be a no-op"
);
}
let (hit_router, mut hit_decoder) = fresh_router_with_shared(&content, &shared);
for (i, id) in product_ids.iter().enumerate() {
let entity = hit_decoder.decode_by_id(*id).expect("decode hit product");
let mesh = hit_router
.process_element(&entity, &mut hit_decoder)
.expect("mesh hit product");
assert_eq!(
fingerprint(&mesh),
baseline[i],
"shared-cache cross-router HIT diverged from the RefCell baseline for product #{id} \
in {rel_path} (#1623 must be byte-identical)"
);
}
}
#[test]
fn shared_mapped_cache_hit_is_byte_identical_to_per_router_baseline() {
for fixture in FIXTURES {
assert_cross_router_hit_is_byte_identical(fixture);
}
}