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
|
- Planner tests now include a deterministic split-tree generator. It builds
|
||||||
valid weighted tiling layouts, derives leaf hierarchy metadata, mutates them
|
valid weighted tiling layouts, derives leaf hierarchy metadata, mutates them
|
||||||
through supported transitions, and runs the real planner plus exact validator.
|
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:
|
Tests:
|
||||||
|
|
||||||
|
|
@ -258,6 +261,7 @@ Tests:
|
||||||
- interruption restarts only affected phase groups
|
- interruption restarts only affected phase groups
|
||||||
- reversing direction produces equivalent motion in reverse
|
- reversing direction produces equivalent motion in reverse
|
||||||
- accepted and rejected plans expose deterministic strategy explanations
|
- 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
|
- child waits for parent/container-space phases when moving upward into a
|
||||||
toplevel peer position
|
toplevel peer position
|
||||||
- mono-mode tab switches do not animate, while entering/exiting mono can animate
|
- 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) {
|
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);
|
let req = generated_request(old, new, bounds);
|
||||||
assert!(!overlaps(req.windows.iter().map(|window| window.from)));
|
assert!(!overlaps(req.windows.iter().map(|window| window.from)));
|
||||||
assert!(!overlaps(req.windows.iter().map(|window| window.to)));
|
assert!(!overlaps(req.windows.iter().map(|window| window.to)));
|
||||||
let plan = plan_no_overlap(&req).unwrap();
|
let first = plan_no_overlap_explained(&req).unwrap();
|
||||||
assert!(validate_plan_continuous(&req, &plan));
|
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 {
|
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));
|
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]
|
#[test]
|
||||||
fn stack_extraction_creates_space_before_moving_child() {
|
fn stack_extraction_creates_space_before_moving_child() {
|
||||||
let req = request(vec![
|
let req = request(vec![
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue