1
0
Fork 0
forked from wry/wry

Assert multiphase rejection diagnostics

This commit is contained in:
atagen 2026-05-21 21:16:54 +10:00
parent 332a7468f6
commit 01d1545c40
2 changed files with 92 additions and 8 deletions

View file

@ -245,6 +245,9 @@ Current pure planner status:
- Multiphase planning also has an explained-plan entry point. Accepted plans - Multiphase planning also has an explained-plan entry point. Accepted plans
report the deterministic strategy, phase reasons, participating nodes, and report the deterministic strategy, phase reasons, participating nodes, and
validation result; rejected plans report every attempted strategy and failure. validation result; rejected plans report every attempted strategy and failure.
- Rejection diagnostics are treated as contractual test output for unsupported
patterns and analytically invalid candidate plans, including attempted strategy
order and exact validation failure.
- 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.
@ -262,6 +265,7 @@ Tests:
- 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 - bounded generated split-tree corpus produces identical plans on repeated runs
- unsupported and invalid candidate plans produce exact expected diagnostics
- 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

View file

@ -1538,6 +1538,49 @@ mod tests {
.to .to
} }
fn no_pattern_attempts(direction: PlanDirection) -> Vec<RejectedStrategy> {
vec![
RejectedStrategy {
direction,
strategy: PlanStrategy::SingleAction,
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::HierarchyOrderedScales,
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::SwapLanes {
axis: PhaseAxis::Horizontal,
},
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::SwapLanes {
axis: PhaseAxis::Vertical,
},
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::SpaceThenOrthogonalGrowth {
axis: PhaseAxis::Horizontal,
},
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::SpaceThenOrthogonalGrowth {
axis: PhaseAxis::Vertical,
},
reason: MultiphasePlanFailure::NoPattern,
},
]
}
#[test] #[test]
fn horizontal_swap_shrinks_moves_then_grows_without_overlap() { fn horizontal_swap_shrinks_moves_then_grows_without_overlap() {
let req = request(vec![ let req = request(vec![
@ -2136,14 +2179,9 @@ mod tests {
let diagnostic = plan_no_overlap_with_diagnostics(&req).unwrap_err(); let diagnostic = plan_no_overlap_with_diagnostics(&req).unwrap_err();
assert_eq!(diagnostic.forward, MultiphasePlanFailure::NoPattern); assert_eq!(diagnostic.forward, MultiphasePlanFailure::NoPattern);
assert_eq!(diagnostic.reverse, Some(MultiphasePlanFailure::NoPattern)); assert_eq!(diagnostic.reverse, Some(MultiphasePlanFailure::NoPattern));
assert!( let mut expected = no_pattern_attempts(PlanDirection::Forward);
diagnostic expected.extend(no_pattern_attempts(PlanDirection::Reverse));
.attempted assert_eq!(diagnostic.attempted, expected);
.iter()
.any(|attempt| attempt.direction == PlanDirection::Forward
&& attempt.strategy == PlanStrategy::SingleAction
&& attempt.reason == MultiphasePlanFailure::NoPattern)
);
} }
#[test] #[test]
@ -2176,6 +2214,48 @@ mod tests {
)); ));
} }
#[test]
fn diagnostics_report_candidate_validation_rejections() {
let req = request(vec![
MultiphaseWindow {
node_id: id(1),
from: rect(0, 0, 60, 60),
to: rect(180, 0, 240, 60),
hierarchy: Default::default(),
},
MultiphaseWindow {
node_id: id(2),
from: rect(90, 0, 150, 60),
to: rect(90, 0, 150, 60),
hierarchy: Default::default(),
},
]);
let rejection =
MultiphasePlanFailure::Validation(MultiphaseValidationError::PhaseOverlap {
phase: 0,
a: id(1),
b: id(2),
});
let diagnostic = plan_no_overlap_with_diagnostics(&req).unwrap_err();
assert_eq!(diagnostic.forward, rejection);
assert_eq!(diagnostic.reverse, Some(rejection));
assert_eq!(
diagnostic.attempted[0],
RejectedStrategy {
direction: PlanDirection::Forward,
strategy: PlanStrategy::SingleAction,
reason: rejection,
}
);
assert!(diagnostic.attempted.iter().any(|attempt| *attempt
== RejectedStrategy {
direction: PlanDirection::Reverse,
strategy: PlanStrategy::SingleAction,
reason: rejection,
}));
}
#[test] #[test]
fn hierarchy_metadata_classifies_depth_and_mono_transitions() { fn hierarchy_metadata_classifies_depth_and_mono_transitions() {
let source = MultiphaseHierarchyPosition { let source = MultiphaseHierarchyPosition {