use super::*;
use futures_util::StreamExt;
#[tokio::test]
async fn test_parse_stream_basic() {
let content = r#"
#1=IFCPROJECT('guid',$,$,$,$,$,$,$,$);
#2=IFCWALL('guid2',$,$,$,$,$,$,$);
#3=IFCDOOR('guid3',$,$,$,$,$,$,$);
"#;
let config = StreamConfig::default();
let mut stream = parse_stream(content, config);
let mut events = Vec::new();
while let Some(event) = stream.next().await {
events.push(event);
}
assert!(events.len() >= 5);
match events[0] {
ParseEvent::Started { .. } => {}
_ => panic!("Expected Started event"),
}
match events.last().unwrap() {
ParseEvent::Completed { entity_count, .. } => {
assert_eq!(*entity_count, 3);
}
_ => panic!("Expected Completed event"),
}
}
#[tokio::test]
async fn test_parse_stream_skip_types() {
let content = r#"
#1=IFCPROJECT('guid',$,$,$,$,$,$,$,$);
#2=IFCOWNERHISTORY('guid2',$,$,$,$,$,$,$);
#3=IFCWALL('guid3',$,$,$,$,$,$,$);
"#;
let config = StreamConfig {
skip_types: vec![IfcType::IfcOwnerHistory],
..Default::default()
};
let mut stream = parse_stream(content, config);
let mut entity_count = 0;
while let Some(event) = stream.next().await {
if let ParseEvent::EntityScanned { .. } = event {
entity_count += 1;
}
}
assert_eq!(entity_count, 2);
}
#[tokio::test]
async fn test_parse_stream_only_types() {
let content = r#"
#1=IFCPROJECT('guid',$,$,$,$,$,$,$,$);
#2=IFCWALL('guid2',$,$,$,$,$,$,$);
#3=IFCDOOR('guid3',$,$,$,$,$,$,$);
"#;
let config = StreamConfig {
skip_types: vec![],
only_types: Some(vec![IfcType::IfcWall]),
..Default::default()
};
let mut stream = parse_stream(content, config);
let mut entity_count = 0;
while let Some(event) = stream.next().await {
if let ParseEvent::EntityScanned { .. } = event {
entity_count += 1;
}
}
assert_eq!(entity_count, 1);
}
#[tokio::test]
async fn test_parse_stream_skips_garbage_and_completes() {
let content = r#"
#1=IFCPROJECT('g',$,$,$,$,$,$,$,$);
this is not an entity line at all !!! ;;;
#2=IFCWALL('g2',$,$,$,$,$,$,$);
@%^&*() not valid step
#3=IFCDOOR('g3',$,$,$,$,$,$,$);
"#;
let mut stream = parse_stream(content, StreamConfig::default());
let mut entity_count = 0;
let mut completed = None;
while let Some(event) = stream.next().await {
match event {
ParseEvent::EntityScanned { .. } => entity_count += 1,
ParseEvent::Completed { entity_count: n, .. } => completed = Some(n),
_ => {}
}
}
assert_eq!(entity_count, 3, "scanner should skip garbage and find all 3");
assert_eq!(completed, Some(3), "stream must reach Completed, not truncate");
}
#[tokio::test]
async fn every_entity_is_emitted_across_a_progress_boundary() {
let content: String = (1..=250)
.map(|n| format!("#{n}=IFCWALL('g{n}',$,$,$,$,$,$,$);\n"))
.collect();
let mut stream = parse_stream(content.as_str(), StreamConfig::default());
let mut ids = Vec::new();
let mut progress_after = Vec::new();
let mut completed = None;
while let Some(event) = stream.next().await {
match event {
ParseEvent::EntityScanned { id, .. } => ids.push(id),
ParseEvent::Progress {
entities_processed, ..
} => progress_after.push((ids.last().copied(), entities_processed)),
ParseEvent::Completed { entity_count, .. } => completed = Some(entity_count),
_ => {}
}
}
assert_eq!(ids, (1..=250).collect::<Vec<u32>>());
assert_eq!(completed, Some(ids.len()));
assert_eq!(progress_after, vec![(Some(100), 100), (Some(200), 200)]);
}
#[tokio::test]
async fn a_progress_event_owed_by_the_last_entity_precedes_completed() {
let content: String = (1..=4)
.map(|n| format!("#{n}=IFCWALL('g{n}',$,$,$,$,$,$,$);\n"))
.collect();
let config = StreamConfig {
progress_interval: 4,
..Default::default()
};
let mut stream = parse_stream(content.as_str(), config);
let mut kinds = Vec::new();
while let Some(event) = stream.next().await {
kinds.push(match event {
ParseEvent::Started { .. } => "started",
ParseEvent::EntityScanned { .. } => "entity",
ParseEvent::Progress { .. } => "progress",
ParseEvent::Completed { .. } => "completed",
_ => "other",
});
}
assert_eq!(
kinds,
["started", "entity", "entity", "entity", "entity", "progress", "completed"]
);
}