use ifc_lite_processing::{classify_type_name, scan_shard, scan_shard_classified};
fn keyword_at(content: &[u8], start: usize, end: usize) -> &str {
let span = &content[start..end.min(content.len())];
let eq = span.iter().position(|&b| b == b'=').map(|p| p + 1).unwrap_or(0);
let kw_end = span[eq..]
.iter()
.position(|&b| b == b'(')
.map(|p| eq + p)
.unwrap_or(span.len());
std::str::from_utf8(&span[eq..kw_end]).unwrap_or("").trim()
}
fn synthetic_ifc() -> String {
let mut content = String::from(
"ISO-10303-21;\nHEADER;\nENDSEC;\nDATA;\n#1=IFCPROJECT('0$ScRe4drECQ4DMSqUjd6d',$,'P',$,$,$,$,(#2),#3);\n",
);
for id in 2..=60u32 {
if id % 3 == 0 {
let long_name = "N".repeat(80);
content.push_str(&format!(
"#{id}=IFCWALL('{long_name}{id}',$,$,$,$,$,$,$);\n"
));
} else {
content.push_str(&format!("#{id}=IFCDOOR('g{id}',$,$,$,$,$,$,$);\n"));
}
}
content.push_str("ENDSEC;\nEND-ISO-10303-21;\n");
for keyword in ["IFCPROJECT", "IFCWALL", "IFCDOOR"] {
assert!(
!ifc_lite_core::is_representationless_spatial_container_by_name(keyword),
"fixture keyword {keyword} takes the #1910 instance-level exception, so \
classify_type_name is no longer a valid oracle for its class byte"
);
}
content
}
fn assert_shard_scans_match(content: &[u8], range_start: usize, range_end: usize, label: &str) {
let (plain_records, plain_handoff) = scan_shard(content, range_start, range_end);
let (classified_records, classes, classified_handoff) =
scan_shard_classified(content, range_start, range_end);
assert_eq!(
plain_records, classified_records,
"scan_shard vs scan_shard_classified record mismatch for {label} \
(range [{range_start}, {range_end}))"
);
assert_eq!(
plain_handoff, classified_handoff,
"scan_shard vs scan_shard_classified handoff mismatch for {label} \
(range [{range_start}, {range_end}))"
);
assert_eq!(
classes.len(),
classified_records.len(),
"scan_shard_classified class column length must match its record count for \
{label} (range [{range_start}, {range_end})) — the host indexes classes \
positionally against the records"
);
for (i, &(id, start, end)) in classified_records.iter().enumerate() {
let keyword = keyword_at(content, start, end);
assert_eq!(
classes[i],
classify_type_name(keyword),
"class byte at index {i} does not belong to the record at that index \
(#{id} {keyword}) for {label} (range [{range_start}, {range_end})) — \
the class column is misaligned with the records"
);
}
}
#[test]
fn single_shard_whole_file_matches() {
let content = synthetic_ifc();
let bytes = content.as_bytes();
assert_shard_scans_match(bytes, 0, bytes.len(), "single-shard-whole-file");
}
#[test]
fn shard_boundary_mid_entity_handoff_matches() {
let content = synthetic_ifc();
let bytes = content.as_bytes();
let len = bytes.len();
let mut checked = 0usize;
for split in 1..len {
assert_shard_scans_match(bytes, 0, split, &format!("first-shard@{split}"));
assert_shard_scans_match(bytes, split, len, &format!("second-shard@{split}"));
checked += 1;
}
assert!(
checked > 100,
"sweep must exercise well over 100 boundary positions to have confidence \
some land mid-entity; only checked {checked}"
);
}