use std::rc::Rc;
use xfa_layout_engine::trace::{sites, with_sink, Phase, Reason, RecordingSink, TraceEvent};
fn record_sites() -> Vec<TraceEvent> {
let sink: Rc<RecordingSink> = Rc::new(RecordingSink::new());
with_sink(sink.clone(), || {
sites::bind(
"form1.subform",
Reason::DataCountMatchesInitial,
"3 instances created from 3 data records",
);
sites::occur("form1.subform.row", Reason::SubformMaterialisedFromData, 3);
sites::presence(
"form1.subform.field_b",
Reason::PresenceHidden,
"presence='hidden'",
);
sites::paginate(
"form1.section_c",
Reason::PaginateDeferToNextPageMinH,
267.3,
380.7,
);
sites::suppress(
Reason::SuppressEmptyDataPageDropped,
3,
"drop empty data-bound page under form-DOM cap",
);
});
sink.events()
}
#[test]
fn five_hot_phase_helpers_emit_distinct_events() {
let events = record_sites();
assert_eq!(events.len(), 5, "expected one event per helper call");
let phases: Vec<_> = events.iter().map(|e| e.phase).collect();
assert_eq!(
phases,
vec![
Phase::Bind,
Phase::Occur,
Phase::Presence,
Phase::Paginate,
Phase::Suppress,
]
);
}
#[test]
fn bind_event_carries_som_decision_and_source() {
let events = record_sites();
let e = &events[0];
assert_eq!(e.phase, Phase::Bind);
assert_eq!(e.reason, Reason::DataCountMatchesInitial);
assert_eq!(e.som.as_deref(), Some("form1.subform"));
assert!(e
.decision
.as_deref()
.map(|d| d.contains("3 instances"))
.unwrap_or(false));
assert_eq!(e.source.as_deref(), Some("xfa_layout_engine::form::bind"));
}
#[test]
fn occur_event_carries_count_in_input() {
let events = record_sites();
let e = &events[1];
assert_eq!(e.phase, Phase::Occur);
assert_eq!(e.reason, Reason::SubformMaterialisedFromData);
assert_eq!(e.input.as_deref(), Some("count=3"));
}
#[test]
fn presence_event_uses_presence_phase() {
let events = record_sites();
let e = &events[2];
assert_eq!(e.phase, Phase::Presence);
assert_eq!(e.reason, Reason::PresenceHidden);
assert_eq!(e.som.as_deref(), Some("form1.subform.field_b"));
}
#[test]
fn paginate_event_carries_geometry_in_input() {
let events = record_sites();
let e = &events[3];
assert_eq!(e.phase, Phase::Paginate);
assert_eq!(e.reason, Reason::PaginateDeferToNextPageMinH);
let input = e.input.as_deref().unwrap();
assert!(input.contains("available_h=267.3000"));
assert!(input.contains("needed_h=380.7000"));
}
#[test]
fn suppress_event_carries_page_index_in_input() {
let events = record_sites();
let e = &events[4];
assert_eq!(e.phase, Phase::Suppress);
assert_eq!(e.reason, Reason::SuppressEmptyDataPageDropped);
assert_eq!(e.input.as_deref(), Some("page_index=3"));
}
#[test]
fn no_events_when_sink_uninstalled() {
sites::bind("a", Reason::Unspecified, "x");
sites::occur("a", Reason::Unspecified, 0);
sites::presence("a", Reason::Unspecified, "x");
sites::paginate("a", Reason::Unspecified, 0.0, 0.0);
sites::suppress(Reason::Unspecified, 0, "x");
}
#[test]
fn canonical_json_of_recorded_events_is_byte_stable() {
let sink: Rc<RecordingSink> = Rc::new(RecordingSink::new());
with_sink(sink.clone(), || {
sites::bind("form1", Reason::DataCountMatchesInitial, "ok");
sites::paginate("form1.x", Reason::PaginateSplit, 100.0, 50.0);
});
let a = sink.to_canonical_json();
let b = sink.to_canonical_json();
assert_eq!(a, b);
assert!(a.contains("\"phase\": \"bind\""));
assert!(a.contains("\"phase\": \"paginate\""));
}