#![allow(clippy::unwrap_used, clippy::panic)]
mod helpers;
use helpers::build_row_tree;
use panes::diff::diff;
use panes::runtime::LayoutRuntime;
use panes::{LayoutTree, PanelId, Rect, fixed, grow};
#[test]
fn diff_identical_layouts_all_unchanged() {
let (tree, pids) = build_row_tree(2, grow(1.0));
let old = tree.resolve(100.0, 100.0).unwrap();
let new = tree.resolve(100.0, 100.0).unwrap();
let scratch = diff(&old, &new);
let d = scratch.as_diff();
assert!(d.added.is_empty());
assert!(d.removed.is_empty());
assert!(d.moved.is_empty());
assert!(d.resized.is_empty());
assert_eq!(d.unchanged.len(), 2);
assert!(d.unchanged.contains(&pids[0]));
assert!(d.unchanged.contains(&pids[1]));
}
#[test]
fn diff_removed_panel() {
let mut tree = LayoutTree::new();
let (_, n0) = tree.add_panel("p0", grow(1.0)).unwrap();
let (p1, n1) = tree.add_panel("p1", grow(1.0)).unwrap();
let (_, n2) = tree.add_panel("p2", grow(1.0)).unwrap();
let root = tree.add_row(0.0, vec![n0, n1, n2]).unwrap();
tree.set_root(root);
let old = tree.resolve(90.0, 100.0).unwrap();
tree.remove_panel(p1).unwrap();
let new = tree.resolve(90.0, 100.0).unwrap();
let scratch = diff(&old, &new);
let d = scratch.as_diff();
assert_eq!(d.removed.len(), 1);
assert!(d.removed.contains(&p1));
assert_eq!(d.resized.len(), 2);
assert!(d.added.is_empty());
}
#[test]
fn diff_resized_panels() {
let (tree, pids) = build_row_tree(2, grow(1.0));
let old = tree.resolve(100.0, 100.0).unwrap();
let new = tree.resolve(200.0, 100.0).unwrap();
let scratch = diff(&old, &new);
let d = scratch.as_diff();
assert_eq!(d.resized.len(), 2);
let p0_resize = d.resized.iter().find(|c| c.id == pids[0]).unwrap();
assert_eq!(
p0_resize.from,
Rect {
x: 0.0,
y: 0.0,
w: 50.0,
h: 100.0
}
);
assert_eq!(
p0_resize.to,
Rect {
x: 0.0,
y: 0.0,
w: 100.0,
h: 100.0
}
);
assert_eq!(d.moved.len(), 1);
let p1_move = d.moved.iter().find(|c| c.id == pids[1]).unwrap();
assert_eq!(p1_move.from.x, 50.0);
assert_eq!(p1_move.to.x, 100.0);
assert!(d.added.is_empty());
assert!(d.removed.is_empty());
assert!(d.unchanged.is_empty());
}
#[test]
fn diff_moved_not_resized() {
let mut tree = LayoutTree::new();
let (p0, n0) = tree.add_panel("p0", fixed(20.0)).unwrap();
let (_, n1) = tree.add_panel("p1", fixed(20.0)).unwrap();
let (p2, n2) = tree.add_panel("p2", fixed(20.0)).unwrap();
let root = tree.add_row(0.0, vec![n0, n1, n2]).unwrap();
tree.set_root(root);
let old = tree.resolve(60.0, 100.0).unwrap();
tree.move_panel(p0, panes::Position::After(p2)).unwrap();
let new = tree.resolve(60.0, 100.0).unwrap();
let scratch = diff(&old, &new);
let d = scratch.as_diff();
assert_eq!(d.moved.len(), 3);
assert!(d.resized.is_empty());
assert!(d.added.is_empty());
assert!(d.removed.is_empty());
}
#[test]
fn diff_moved_and_resized() {
let mut tree = LayoutTree::new();
let (p0, n0) = tree.add_panel("p0", grow(1.0)).unwrap();
let (_, n1) = tree.add_panel("p1", grow(1.0)).unwrap();
let (p2, n2) = tree.add_panel("p2", grow(1.0)).unwrap();
let root = tree.add_row(0.0, vec![n0, n1, n2]).unwrap();
tree.set_root(root);
let old = tree.resolve(60.0, 100.0).unwrap();
tree.move_panel(p0, panes::Position::After(p2)).unwrap();
let new = tree.resolve(90.0, 100.0).unwrap();
let scratch = diff(&old, &new);
let d = scratch.as_diff();
let moved_ids: Vec<PanelId> = d.moved.iter().map(|c| c.id).collect();
let resized_ids: Vec<PanelId> = d.resized.iter().map(|c| c.id).collect();
assert!(moved_ids.contains(&p0));
assert!(resized_ids.contains(&p0));
}
#[test]
fn diff_first_frame() {
let (tree, pids) = build_row_tree(2, grow(1.0));
let layout = tree.resolve(100.0, 100.0).unwrap();
let scratch = diff(&layout, &layout);
let d = scratch.as_diff();
assert!(d.added.is_empty());
assert!(d.removed.is_empty());
assert!(d.moved.is_empty());
assert!(d.resized.is_empty());
assert_eq!(d.unchanged.len(), 2);
assert!(d.unchanged.contains(&pids[0]));
assert!(d.unchanged.contains(&pids[1]));
}
#[test]
fn same_panel_diff_ignores_retired_panel_holes_after_remove_and_resize() {
let mut tree = LayoutTree::new();
let (_, n0) = tree.add_panel("a", grow(1.0)).unwrap();
let (hole1, n_hole1) = tree.add_panel("tmp1", grow(1.0)).unwrap();
let (_, n1) = tree.add_panel("b", grow(1.0)).unwrap();
let (hole2, n_hole2) = tree.add_panel("tmp2", grow(1.0)).unwrap();
let (_, n2) = tree.add_panel("c", grow(1.0)).unwrap();
let root = tree
.add_row(0.0, vec![n0, n_hole1, n1, n_hole2, n2])
.unwrap();
tree.set_root(root);
tree.remove_panel(hole1).unwrap();
tree.remove_panel(hole2).unwrap();
let mut rt = LayoutRuntime::new(tree);
rt.resolve(200.0, 200.0).unwrap();
rt.resolve(200.0, 200.0).unwrap();
let frame = rt.resolve(300.0, 200.0).unwrap();
let d = rt.last_diff();
assert!(d.added.is_empty(), "expected no added, got {:?}", d.added);
assert!(
d.removed.is_empty(),
"expected no removed, got {:?}",
d.removed
);
let live_count = frame.layout().panels().count();
let mut classified_ids: Vec<PanelId> = Vec::new();
classified_ids.extend(d.moved.iter().map(|c| c.id));
classified_ids.extend(d.resized.iter().map(|c| c.id));
classified_ids.extend(d.unchanged.iter().copied());
classified_ids.sort_by_key(|p| p.raw());
classified_ids.dedup();
assert_eq!(
classified_ids.len(),
live_count,
"diff classified {} unique panels but {live_count} are live",
classified_ids.len(),
);
}
#[test]
fn topology_dirty_frames_still_report_add_remove_over_live_panel_iteration_changes() {
let mut tree = LayoutTree::new();
let (_, n0) = tree.add_panel("a", grow(1.0)).unwrap();
let (_, n1) = tree.add_panel("b", grow(1.0)).unwrap();
let root = tree.add_row(0.0, vec![n0, n1]).unwrap();
tree.set_root(root);
let mut rt = LayoutRuntime::new(tree);
rt.resolve(200.0, 200.0).unwrap();
let (new_pid, new_nid) = rt.tree_mut().add_panel("c", grow(1.0)).unwrap();
rt.tree_mut().insert_child_at(root, 2, new_nid).unwrap();
let frame = rt.resolve(200.0, 200.0).unwrap();
let d = rt.last_diff();
assert!(d.added.contains(&new_pid), "new panel should be in added");
assert!(d.removed.is_empty(), "no panels were removed");
let live_count = frame.layout().panels().count();
let mut ids: Vec<PanelId> = Vec::new();
ids.extend(d.added.iter().copied());
ids.extend(d.moved.iter().map(|c| c.id));
ids.extend(d.resized.iter().map(|c| c.id));
ids.extend(d.unchanged.iter().copied());
ids.sort_by_key(|p| p.raw());
ids.dedup();
assert_eq!(ids.len(), live_count, "all live panels must be classified");
rt.tree_mut().remove_panel(new_pid).unwrap();
let frame2 = rt.resolve(200.0, 200.0).unwrap();
let d2 = rt.last_diff();
assert!(
d2.removed.contains(&new_pid),
"removed panel should be in removed"
);
assert!(d2.added.is_empty(), "no panels were added");
let live_count2 = frame2.layout().panels().count();
let mut ids2: Vec<PanelId> = Vec::new();
ids2.extend(d2.moved.iter().map(|c| c.id));
ids2.extend(d2.resized.iter().map(|c| c.id));
ids2.extend(d2.unchanged.iter().copied());
ids2.sort_by_key(|p| p.raw());
ids2.dedup();
assert_eq!(
ids2.len(),
live_count2,
"all live panels must be classified"
);
}