Assert mixed multiphase action boundaries
This commit is contained in:
parent
632873ec5a
commit
a5845fb293
2 changed files with 91 additions and 1 deletions
|
|
@ -170,7 +170,9 @@ Core rules:
|
|||
- Each phase is a discrete animation using the full curve.
|
||||
- A phase performs only one action kind per window: move or scale.
|
||||
- Movement and scaling are split by axis.
|
||||
- No diagonal motion.
|
||||
- No diagonal motion. Mixed-action phases may combine different per-window
|
||||
actions, but no single window may move or resize on more than one axis in one
|
||||
step.
|
||||
- A window or synchronized group owns its own timeline.
|
||||
- New layout changes interrupt only windows/groups with changed destinations.
|
||||
- Current hierarchy and target hierarchy both matter. The planner must know
|
||||
|
|
@ -270,6 +272,7 @@ Tests:
|
|||
- bounded generated split-tree corpus produces identical plans on repeated runs
|
||||
- unsupported and invalid candidate plans produce exact expected diagnostics
|
||||
- mixed-action phases are accepted only under exact continuous validation
|
||||
- diagonal per-window motion remains a hard rejection even inside mixed phases
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -1910,6 +1910,56 @@ mod tests {
|
|||
assert!(validate_plan_continuous(&req, &planned.plan));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_single_phase_accepts_move_and_scale_when_proven() {
|
||||
let req = request(vec![
|
||||
window(1, rect(0, 0, 80, 80), rect(40, 0, 120, 80)),
|
||||
window(2, rect(200, 0, 280, 80), rect(200, 0, 280, 120)),
|
||||
]);
|
||||
let planned = plan_no_overlap_explained(&req).unwrap();
|
||||
|
||||
assert_eq!(planned.explanation.strategy, PlanStrategy::MixedSinglePhase);
|
||||
assert_eq!(
|
||||
planned.plan.phases[0].action,
|
||||
MultiphasePhaseAction::Mixed(vec![
|
||||
PhaseAction {
|
||||
kind: PhaseKind::Move,
|
||||
axis: PhaseAxis::Horizontal,
|
||||
},
|
||||
PhaseAction {
|
||||
kind: PhaseKind::Scale,
|
||||
axis: PhaseAxis::Vertical,
|
||||
},
|
||||
])
|
||||
);
|
||||
assert_eq!(
|
||||
planned.explanation.phases[0].reason,
|
||||
PhaseReason::MixedAxisActions
|
||||
);
|
||||
assert_eq!(planned.explanation.phases[0].nodes, vec![id(1), id(2)]);
|
||||
assert!(validate_plan_continuous(&req, &planned.plan));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_single_phase_still_rejects_diagonal_per_window_motion() {
|
||||
let req = request(vec![
|
||||
window(1, rect(0, 0, 80, 80), rect(40, 40, 120, 120)),
|
||||
window(2, rect(200, 0, 280, 80), rect(200, 0, 280, 120)),
|
||||
]);
|
||||
|
||||
assert_eq!(plan_no_overlap(&req), Err(MultiphaseError::NoPlan));
|
||||
let diagnostic = plan_no_overlap_with_diagnostics(&req).unwrap_err();
|
||||
let rejection = MultiphasePlanFailure::InvalidPhaseStep {
|
||||
action: PhaseAction {
|
||||
kind: PhaseKind::Scale,
|
||||
axis: PhaseAxis::Horizontal,
|
||||
},
|
||||
node_id: id(1),
|
||||
};
|
||||
assert_eq!(diagnostic.forward, rejection);
|
||||
assert_eq!(diagnostic.reverse, Some(rejection));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated_nested_size_redistribution_scales_parent_axis_first() {
|
||||
let old = split(
|
||||
|
|
@ -2518,6 +2568,43 @@ mod tests {
|
|||
assert!(validate_plan_continuous(&req, &plan));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn continuous_validation_rejects_mixed_phase_action_count_mismatch() {
|
||||
let req = request(vec![
|
||||
window(1, rect(0, 0, 40, 40), rect(40, 0, 80, 40)),
|
||||
window(2, rect(100, 0, 140, 40), rect(100, 0, 140, 80)),
|
||||
]);
|
||||
let plan = MultiphasePlan {
|
||||
phases: vec![MultiphasePhase {
|
||||
action: MultiphasePhaseAction::Mixed(vec![PhaseAction {
|
||||
kind: PhaseKind::Move,
|
||||
axis: PhaseAxis::Horizontal,
|
||||
}]),
|
||||
steps: vec![
|
||||
MultiphaseStep {
|
||||
node_id: id(1),
|
||||
from: rect(0, 0, 40, 40),
|
||||
to: rect(40, 0, 80, 40),
|
||||
},
|
||||
MultiphaseStep {
|
||||
node_id: id(2),
|
||||
from: rect(100, 0, 140, 40),
|
||||
to: rect(100, 0, 140, 80),
|
||||
},
|
||||
],
|
||||
}],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
validate_plan_continuous_diagnostic(&req, &plan),
|
||||
Err(MultiphaseValidationError::PhaseActionCount {
|
||||
phase: 0,
|
||||
actions: 1,
|
||||
steps: 2,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn continuous_validation_rejects_stale_step_start_rect() {
|
||||
let req = request(vec![MultiphaseWindow {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue