mod common;
use common::quick_metadata::{bootstrap, child, count, tree};
use ifc_lite_processing::determinism::FIXTURE_IFC;
use ifc_lite_processing::QuickMetadataPrunedEdgeKind::{BackEdge, SecondParent, SiblingRepeat};
use ifc_lite_processing::{
QuickMetadataBootstrap, QuickMetadataPrunedEdge, QuickMetadataSpatialNode,
};
const SPATIAL_ENTITIES: &str = "\
#900=IFCSITE('0DeterminismSite0000A',$,'Site',$,$,#20,$,$,.ELEMENT.,$,$,$,$,$);
#901=IFCBUILDING('0DeterminismBldg0000A',$,'Building',$,$,#20,$,$,.ELEMENT.,$,$,$);
#902=IFCBUILDINGSTOREY('0DeterminismStorey00A',$,'Level 1',$,$,#20,$,$,.ELEMENT.,0.);
#914=IFCRELCONTAINEDINSPATIALSTRUCTURE('0DeterminismCont000A',$,$,$,(#100,#400,#500,#600),#902);
";
const DUPLICATED_AGGREGATES: &str = "\
#909=IFCRELAGGREGATES('0DeterminismAggBack00A',$,$,$,#901,(#900));
#910=IFCRELAGGREGATES('0DeterminismAggPS000A',$,$,$,#1,(#900,#900,#900));
#911=IFCRELAGGREGATES('0DeterminismAggSB000A',$,$,$,#900,(#901,#901,#901));
#912=IFCRELAGGREGATES('0DeterminismAggBS000A',$,$,$,#901,(#902,#902,#902));
#913=IFCRELAGGREGATES('0DeterminismAggSS000A',$,$,$,#900,(#902));
";
const CLEAN_AGGREGATES: &str = "\
#910=IFCRELAGGREGATES('0DeterminismAggPS000A',$,$,$,#1,(#900));
#911=IFCRELAGGREGATES('0DeterminismAggSB000A',$,$,$,#900,(#901));
#912=IFCRELAGGREGATES('0DeterminismAggBS000A',$,$,$,#901,(#902));
";
fn fixture_with(aggregates: &str) -> String {
FIXTURE_IFC.replacen(
"ENDSEC;\nEND-ISO",
&format!("{SPATIAL_ENTITIES}{aggregates}ENDSEC;\nEND-ISO"),
1,
)
}
fn assert_single_chain(root: &QuickMetadataSpatialNode) {
assert_eq!(root.summary.express_id, 1, "IfcProject #1 is the root");
for id in [900, 901, 902] {
assert_eq!(
count(root, id),
1,
"node #{id} must be placed exactly once"
);
}
let storey = child(child(child(root, 900), 901), 902);
assert_eq!(
storey.elements.len(),
4,
"storey #902 keeps its four contained elements"
);
}
#[test]
fn duplicated_aggregate_children_are_placed_once() {
assert_single_chain(tree(&bootstrap(&fixture_with(DUPLICATED_AGGREGATES))));
}
#[test]
fn pruned_aggregate_edges_are_reported_with_their_ids() {
let bootstrap = bootstrap(&fixture_with(DUPLICATED_AGGREGATES));
let edge = |parent_express_id, child_express_id, kind| QuickMetadataPrunedEdge {
parent_express_id,
child_express_id,
kind,
};
assert_eq!(
bootstrap.pruned_aggregate_edges,
[
edge(901, 900, BackEdge),
edge(901, 902, SiblingRepeat),
edge(901, 902, SiblingRepeat),
edge(900, 901, SiblingRepeat),
edge(900, 901, SiblingRepeat),
edge(900, 902, SecondParent),
edge(1, 900, SiblingRepeat),
edge(1, 900, SiblingRepeat),
]
);
let json = serde_json::to_value(&bootstrap).expect("bootstrap serialises");
assert_eq!(
json["pruned_aggregate_edges"][0],
serde_json::json!({ "parent_express_id": 901, "child_express_id": 900, "kind": "back_edge" })
);
}
#[test]
fn clean_aggregate_tree_reports_no_pruned_edges() {
let bootstrap = bootstrap(&fixture_with(CLEAN_AGGREGATES));
assert_single_chain(tree(&bootstrap));
assert!(
bootstrap.pruned_aggregate_edges.is_empty(),
"a clean hierarchy prunes nothing, got {:?}",
bootstrap.pruned_aggregate_edges
);
let json = serde_json::to_value(&bootstrap).expect("bootstrap serialises");
assert!(json.get("pruned_aggregate_edges").is_none(), "{json}");
let old: QuickMetadataBootstrap =
serde_json::from_value(json).expect("a payload without the field deserialises");
assert!(old.pruned_aggregate_edges.is_empty());
}