Enumerate deterministic split-tree plans
This commit is contained in:
parent
511e188d16
commit
332a7468f6
2 changed files with 141 additions and 2 deletions
|
|
@ -248,6 +248,9 @@ Current pure planner status:
|
|||
- Planner tests now include a deterministic split-tree generator. It builds
|
||||
valid weighted tiling layouts, derives leaf hierarchy metadata, mutates them
|
||||
through supported transitions, and runs the real planner plus exact validator.
|
||||
- The generated tests also include a bounded corpus of supported split-tree
|
||||
transitions across both axes and directions. Each case is planned twice and
|
||||
compared exactly to catch nondeterministic planner output.
|
||||
|
||||
Tests:
|
||||
|
||||
|
|
@ -258,6 +261,7 @@ Tests:
|
|||
- interruption restarts only affected phase groups
|
||||
- reversing direction produces equivalent motion in reverse
|
||||
- accepted and rejected plans expose deterministic strategy explanations
|
||||
- bounded generated split-tree corpus produces identical plans on repeated runs
|
||||
- child waits for parent/container-space phases when moving upward into a
|
||||
toplevel peer position
|
||||
- mono-mode tab switches do not animate, while entering/exiting mono can animate
|
||||
|
|
|
|||
|
|
@ -1473,11 +1473,47 @@ mod tests {
|
|||
}
|
||||
|
||||
fn assert_generated_case_plans(old: &TestTree, new: &TestTree, bounds: Rect) {
|
||||
assert_generated_case_plans_deterministically(old, new, bounds);
|
||||
}
|
||||
|
||||
fn assert_generated_case_plans_deterministically(
|
||||
old: &TestTree,
|
||||
new: &TestTree,
|
||||
bounds: Rect,
|
||||
) -> MultiphasePlanned {
|
||||
let req = generated_request(old, new, bounds);
|
||||
assert!(!overlaps(req.windows.iter().map(|window| window.from)));
|
||||
assert!(!overlaps(req.windows.iter().map(|window| window.to)));
|
||||
let plan = plan_no_overlap(&req).unwrap();
|
||||
assert!(validate_plan_continuous(&req, &plan));
|
||||
let first = plan_no_overlap_explained(&req).unwrap();
|
||||
let second = plan_no_overlap_explained(&req).unwrap();
|
||||
assert_eq!(first, second);
|
||||
assert_eq!(first.plan.phases.len(), first.explanation.phases.len());
|
||||
assert_eq!(
|
||||
first.explanation.validation,
|
||||
ValidationExplanation::passed()
|
||||
);
|
||||
for phase in &first.explanation.phases {
|
||||
assert!(phase.nodes.windows(2).all(|pair| pair[0].0 < pair[1].0));
|
||||
}
|
||||
assert!(validate_plan_continuous(&req, &first.plan));
|
||||
first
|
||||
}
|
||||
|
||||
fn bounds_for_axis(axis: PhaseAxis) -> Rect {
|
||||
match axis {
|
||||
PhaseAxis::Horizontal => rect(0, 0, 400, 100),
|
||||
PhaseAxis::Vertical => rect(0, 0, 100, 400),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_generated_case_bidirectional(
|
||||
cases: &mut Vec<(TestTree, TestTree, Rect)>,
|
||||
old: TestTree,
|
||||
new: TestTree,
|
||||
bounds: Rect,
|
||||
) {
|
||||
cases.push((old.clone(), new.clone(), bounds));
|
||||
cases.push((new, old, bounds));
|
||||
}
|
||||
|
||||
fn request(windows: Vec<MultiphaseWindow>) -> MultiphaseRequest {
|
||||
|
|
@ -1798,6 +1834,105 @@ mod tests {
|
|||
assert_generated_case_plans(&vertical_new, &vertical_old, rect(0, 0, 100, 400));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bounded_generated_supported_split_tree_corpus_is_deterministic() {
|
||||
let mut cases = vec![];
|
||||
for axis in [PhaseAxis::Horizontal, PhaseAxis::Vertical] {
|
||||
let child_axis = axis.other();
|
||||
let bounds = bounds_for_axis(axis);
|
||||
|
||||
push_generated_case_bidirectional(
|
||||
&mut cases,
|
||||
split(10, axis, &[1, 1], vec![leaf(1), leaf(2)]),
|
||||
split(10, axis, &[1, 1], vec![leaf(2), leaf(1)]),
|
||||
bounds,
|
||||
);
|
||||
push_generated_case_bidirectional(
|
||||
&mut cases,
|
||||
split(10, axis, &[1, 1, 1], vec![leaf(1), leaf(2), leaf(3)]),
|
||||
split(10, axis, &[1, 2, 1], vec![leaf(1), leaf(2), leaf(3)]),
|
||||
bounds,
|
||||
);
|
||||
push_generated_case_bidirectional(
|
||||
&mut cases,
|
||||
split(
|
||||
10,
|
||||
axis,
|
||||
&[1, 1],
|
||||
vec![
|
||||
leaf(1),
|
||||
split(11, child_axis, &[1, 1], vec![leaf(2), leaf(3)]),
|
||||
],
|
||||
),
|
||||
split(10, axis, &[1, 2, 1], vec![leaf(1), leaf(2), leaf(3)]),
|
||||
bounds,
|
||||
);
|
||||
push_generated_case_bidirectional(
|
||||
&mut cases,
|
||||
split(
|
||||
10,
|
||||
axis,
|
||||
&[1, 1],
|
||||
vec![
|
||||
split(11, child_axis, &[1, 1], vec![leaf(1), leaf(2)]),
|
||||
leaf(3),
|
||||
],
|
||||
),
|
||||
split(10, axis, &[1, 2, 1], vec![leaf(1), leaf(2), leaf(3)]),
|
||||
bounds,
|
||||
);
|
||||
push_generated_case_bidirectional(
|
||||
&mut cases,
|
||||
split(
|
||||
10,
|
||||
axis,
|
||||
&[1, 1],
|
||||
vec![
|
||||
leaf(1),
|
||||
split(11, child_axis, &[1, 1], vec![leaf(2), leaf(3)]),
|
||||
],
|
||||
),
|
||||
split(
|
||||
10,
|
||||
axis,
|
||||
&[1, 3],
|
||||
vec![
|
||||
leaf(1),
|
||||
split(11, child_axis, &[3, 1], vec![leaf(2), leaf(3)]),
|
||||
],
|
||||
),
|
||||
bounds,
|
||||
);
|
||||
push_generated_case_bidirectional(
|
||||
&mut cases,
|
||||
split(
|
||||
10,
|
||||
axis,
|
||||
&[1, 1],
|
||||
vec![
|
||||
split(11, child_axis, &[1, 1], vec![leaf(1), leaf(2)]),
|
||||
leaf(3),
|
||||
],
|
||||
),
|
||||
split(
|
||||
10,
|
||||
axis,
|
||||
&[3, 1],
|
||||
vec![
|
||||
split(11, child_axis, &[1, 3], vec![leaf(1), leaf(2)]),
|
||||
leaf(3),
|
||||
],
|
||||
),
|
||||
bounds,
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(cases.len(), 24);
|
||||
for (old, new, bounds) in cases {
|
||||
assert_generated_case_plans_deterministically(&old, &new, bounds);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stack_extraction_creates_space_before_moving_child() {
|
||||
let req = request(vec![
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue