Files
ARCADIA-Studio/harness/experiments/behavior-layout-scene-20260912/lifetime.rs
T

260 lines
9.6 KiB
Rust

//! Real current Scene/lifecycle evaluator, independent of the original fixture.
//! No replacement activation geometry or future peer implementation is used.
use super::*;
use crate::document::Document;
use oa_core::source_project::{ProjectLimits, SourceBundle};
fn lifetime_document() -> (Document, Id) {
let source = include_str!("lifetime.arcadia");
let parsed = language::parse(source).expect("separate lifetime source parses");
let errors: Vec<_> = validation::validate(&parsed)
.into_iter()
.filter(|issue| issue.severity == validation::Severity::Error)
.collect();
assert!(
errors.is_empty(),
"lifetime fixture semantic errors: {errors:?}"
);
let owner = id(&parsed, "lifetime_conversation");
let bundle = SourceBundle {
entry: "lifetime.arcadia".into(),
files: BTreeMap::from([("lifetime.arcadia".into(), source.into())]),
};
let document = Document::from_bundle(bundle.clone(), ProjectLimits::default())
.expect("lifetime fixture is ordinarily admitted");
assert_eq!(document.project(), &parsed);
assert_eq!(document.bundle(), &bundle);
assert_eq!(language::parse(&language::format(&parsed)).unwrap(), parsed);
(document, owner)
}
/// Numeric expectations are hand-derived current-Scene metrics, not proposed
/// permanent spacing rules: Action95, message92, initial occurrence148.
fn assert_lifetime_geometry(
scene: &Scene,
project: &Project,
owner: Id,
birth: f32,
call_y: f32,
death: f32,
) {
let requester = id(project, "requester");
let provider = id(project, "provider");
let create = id(project, "create_receiver");
let call = id(project, "service_call");
let delete = id(project, "delete_receiver");
let left = scene.lanes[&requester];
let right = scene.lanes[&provider];
assert_eq!((left, right), (160., 400.));
assert_eq!(row(scene, id(project, "before_birth")), 148.);
assert_eq!(row(scene, create), birth);
assert_eq!(row(scene, call), call_y);
assert_eq!(row(scene, delete), death);
assert!(
birth > 148.,
"receiver creation is delayed beyond the opening action"
);
assert_eq!(scene.rows[&create].0, owner);
assert_eq!(scene.rows[&delete].0, owner);
// Exercise the actual Rendering bridge, including marker hit identity and
// its clipping contract, rather than rebuilding lifetimes from our rows.
let alive = scene
.lifecycle
.alive_spans(provider, owner, 85., scene.size.height - 20.);
assert_eq!(
alive
.iter()
.map(|span| (span.start, span.end))
.collect::<Vec<_>>(),
vec![(birth, death)]
);
assert!(
scene
.lifecycle
.alive_spans(provider, owner, 85., birth)
.is_empty()
);
assert!(
scene
.lifecycle
.alive_spans(provider, owner, death, scene.size.height - 20.)
.is_empty()
);
let sender_alive = scene
.lifecycle
.alive_spans(requester, owner, 85., scene.size.height - 20.);
assert_eq!(
sender_alive
.iter()
.map(|span| (span.start, span.end))
.collect::<Vec<_>>(),
vec![(85., scene.size.height - 20.)]
);
assert_eq!(
scene.lifecycle.marker_at(Point::new(right, birth)),
Some(create)
);
assert_eq!(
scene.lifecycle.marker_at(Point::new(right, death)),
Some(delete)
);
assert_eq!(scene.hit(Point::new(right, birth)), Some(create));
assert_eq!(scene.hit(Point::new(right, death)), Some(delete));
assert_eq!(
line(scene, create).points,
vec![Point::new(left, birth), Point::new(right - 70., birth)]
);
assert_eq!(
line(scene, call).points,
vec![Point::new(left, call_y), Point::new(right, call_y)]
);
assert_eq!(
line(scene, delete).points,
vec![Point::new(left, death), Point::new(right, death)]
);
assert!(line(scene, create).label_at.y + 22. < birth - 14.);
// Inspect actual activation boxes produced by Scene::activation_sequence.
// A correlated Reply is absent, so death must clip the bar, not close the
// semantic call. Do not accept a new generic minimum-height tail after death.
let bars: Vec<_> = scene
.boxes
.iter()
.filter(|item| item.id == call && item.title.is_empty())
.collect();
assert_eq!(bars.len(), 1);
assert_eq!(
bars[0].rect,
Rectangle::new(
Point::new(right - 4., call_y),
Size::new(8., death - call_y)
)
);
assert_eq!(bars[0].rect.y + bars[0].rect.height, death);
assert!(
bars.iter()
.all(|item| item.rect.y >= birth && item.rect.y + item.rect.height <= death)
);
assert_eq!(scene.open_calls, vec![Point::new(right + 12., death - 16.)]);
assert!(!project.elements.values().any(|element| matches!(
element.shared(),
Some(SharedBody::Occurrence {
body: OccurrenceBody::Exchange {
message_kind: MessageKind::Reply,
..
},
..
})
)));
}
#[test]
fn delayed_create_call_and_delete_have_real_lifetime_markers_and_exact_clipped_activation() {
let (document, owner) = lifetime_document();
let before = document.snapshot();
let source = document.bundle().clone();
let scene = Scene::sequence(document.project(), owner, owner);
assert_lifetime_geometry(&scene, document.project(), owner, 243., 335., 427.);
assert_eq!(scene.size.height, 564.);
assert_eq!(scene.sequence_bounds[&owner], (106., 477.));
same_geometry(&scene, &render(document.project(), owner, &Layout::new()));
let reparsed = language::parse(&language::format(document.project())).unwrap();
same_geometry(&scene, &Scene::sequence(&reparsed, owner, owner));
assert_eq!(document.project(), &before.decoded.project);
assert_eq!(document.bundle(), &source);
assert!(!document.dirty() && !document.can_undo() && !document.can_redo());
}
#[test]
fn moving_create_moves_real_lifetime_and_followers_without_changing_semantics_or_source() {
let (document, owner) = lifetime_document();
let before = document.snapshot();
let source = document.bundle().clone();
let p = document.project();
let create = id(p, "create_receiver");
let provider = id(p, "provider");
let baseline = Scene::sequence(p, owner, owner);
let layout = drag(&Layout::new(), &baseline, create, 60.);
assert_eq!(layout, Layout::from([(create, 303.)]));
let moved = render(p, owner, &layout);
assert_lifetime_geometry(&moved, p, owner, 303., 395., 487.);
assert_eq!(moved.size.height, 624.);
assert_eq!(
moved
.lifecycle
.marker_at(Point::new(moved.lanes[&provider], 243.)),
None
);
assert_eq!(
named_box(&moved, id(p, "before_birth")).rect,
named_box(&baseline, id(p, "before_birth")).rect
);
assert_eq!(
moved.rows.keys().collect::<Vec<_>>(),
baseline.rows.keys().collect::<Vec<_>>()
);
assert_eq!(
moved.lines.iter().map(|line| line.id).collect::<Vec<_>>(),
baseline
.lines
.iter()
.map(|line| line.id)
.collect::<Vec<_>>()
);
let reparsed = language::parse(&language::format(p)).unwrap();
same_geometry(&moved, &render(&reparsed, owner, &layout));
assert_eq!(p, &before.decoded.project);
assert_eq!(document.bundle(), &source);
assert!(!document.dirty() && !document.can_undo() && !document.can_redo());
}
#[test]
fn moving_delete_extends_only_to_actual_death_and_zero_motion_preserves_latent_lifetime_intent() {
let (document, owner) = lifetime_document();
let before = document.snapshot();
let source = document.bundle().clone();
let p = document.project();
let create = id(p, "create_receiver");
let delete = id(p, "delete_receiver");
let provider = id(p, "provider");
let baseline = Scene::sequence(p, owner, owner);
let later_delete = drag(&Layout::new(), &baseline, delete, 48.);
let extended = render(p, owner, &later_delete);
assert_lifetime_geometry(&extended, p, owner, 243., 335., 475.);
assert_eq!(extended.size.height, 612.);
assert_eq!(
extended
.lifecycle
.marker_at(Point::new(extended.lanes[&provider], 427.)),
None
);
assert_eq!(
line(&extended, create).points,
line(&baseline, create).points
);
assert_eq!(
line(&extended, id(p, "service_call")).points,
line(&baseline, id(p, "service_call")).points
);
// Create displacement forces the displayed death to487 while its authored
// request stays427. Both signed zero deltas must preserve that latent value.
let original = Layout::from([(create, 243.), (delete, 427.)]);
let displaced = drag(&original, &baseline, create, 60.);
let frame = render(p, owner, &displaced);
assert_lifetime_geometry(&frame, p, owner, 303., 395., 487.);
assert_eq!(displaced[&delete], 427.);
for delta in [0., -0.] {
let untouched = drag(&displaced, &frame, delete, delta);
assert_eq!(untouched, displaced);
same_geometry(&frame, &render(p, owner, &untouched));
let restored = drag(&untouched, &frame, create, -60.);
assert_eq!(restored, original);
same_geometry(&baseline, &render(p, owner, &restored));
}
assert_eq!(p, &before.decoded.project);
assert_eq!(document.bundle(), &source);
assert!(!document.dirty() && !document.can_undo() && !document.can_redo());
}