tests.rs
raw
use super::*;
use crate::geom::{Affine, Point, Rect, Stroke};
use crate::paint::color::palette::css;
use crate::paint::{Brush, Color};
fn sample_fragment() -> Fragment {
let mut fragment = Fragment::new();
fragment.fill(Rect::new(0.0, 0.0, 100.0, 50.0), css::REBECCA_PURPLE);
fragment.stroke(
Rect::new(10.0, 10.0, 90.0, 40.0),
css::WHITE,
Stroke::new(2.0),
);
fragment
}
#[test]
fn structural_equality() {
assert_eq!(sample_fragment(), sample_fragment());
let mut other = sample_fragment();
other.fill(Rect::new(0.0, 0.0, 1.0, 1.0), css::BLACK);
assert_ne!(sample_fragment(), other);
}
#[test]
fn brush_conversion_from_color() {
let mut fragment = Fragment::new();
fragment.fill(Rect::new(0.0, 0.0, 1.0, 1.0), Color::from_rgb8(1, 2, 3));
let DisplayItem::Fill { brush, .. } = &fragment.items[0] else {
panic!("expected fill");
};
assert_eq!(*brush, Brush::Solid(Color::from_rgb8(1, 2, 3)));
}
#[test]
fn balanced_stack_items() {
let mut fragment = Fragment::new();
assert!(fragment.is_balanced());
fragment.push_clip(Rect::new(0.0, 0.0, 10.0, 10.0));
assert!(!fragment.is_balanced());
fragment.push_transform(Affine::translate((5.0, 5.0)));
fragment.fill(Rect::new(0.0, 0.0, 1.0, 1.0), css::RED);
fragment.pop_transform();
fragment.pop_clip();
assert!(fragment.is_balanced());
}
#[test]
fn mismatched_pop_kind_is_unbalanced() {
let mut fragment = Fragment::new();
fragment.push_clip(Rect::new(0.0, 0.0, 10.0, 10.0));
fragment.pop_transform();
assert!(!fragment.is_balanced());
}
#[test]
fn interleaved_pops_are_unbalanced() {
let mut fragment = Fragment::new();
fragment.push_clip(Rect::new(0.0, 0.0, 10.0, 10.0));
fragment.push_transform(Affine::IDENTITY);
fragment.pop_clip();
fragment.pop_transform();
assert!(!fragment.is_balanced());
}
#[test]
fn store_create_link_remove() {
let mut store = FragmentStore::new();
let child = store.insert(sample_fragment());
let root = store.create();
store
.get_mut(root)
.unwrap()
.child(Affine::translate((20.0, 30.0)), child);
let DisplayItem::Child {
transform,
fragment,
} = &store.get(root).unwrap().items[0]
else {
panic!("expected child item");
};
assert_eq!(*transform * Point::ORIGIN, Point::new(20.0, 30.0));
assert_eq!(store.get(*fragment), Some(&sample_fragment()));
store.remove(child);
assert_eq!(store.get(child), None);
}
#[test]
fn child_inherits_open_scopes() {
// A scroll-area-shaped fragment: clip, offset, child, pop, pop — the child
// sits inside both open scopes and the fragment is still balanced.
let mut store = FragmentStore::new();
let content = store.insert(sample_fragment());
let mut scroller = Fragment::new();
scroller.push_clip(Rect::new(0.0, 0.0, 100.0, 100.0));
scroller.push_transform(Affine::translate((0.0, -25.0)));
scroller.child(Affine::IDENTITY, content);
scroller.pop_transform();
scroller.pop_clip();
assert!(scroller.is_balanced());
}
#[test]
fn shape_to_path_preserves_bounds() {
let rect = Rect::new(0.0, 0.0, 40.0, 20.0);
let shape = Shape::from(rect.to_rounded_rect(4.0));
assert_eq!(shape.bounding_box(), rect);
let path = shape.to_path(0.1);
let bounds = path.bounding_box();
assert!((bounds.x0 - rect.x0).abs() < 0.5 && (bounds.x1 - rect.x1).abs() < 0.5);
}
#[test]
fn epochs_advance_on_mutation_and_vanish_on_removal() {
let mut store = FragmentStore::new();
let a = store.insert(sample_fragment());
let b = store.create();
let epoch_a = store.epoch(a).expect("a exists");
let epoch_b = store.epoch(b).expect("b exists");
assert_ne!(epoch_a, epoch_b, "every stamp is unique");
// Immutable access does not advance the epoch.
let _ = store.get(a);
assert_eq!(store.epoch(a), Some(epoch_a));
// Mutable access advances it, even without a write.
let _ = store.get_mut(a);
let bumped = store.epoch(a).expect("a still exists");
assert!(bumped > epoch_a);
assert_eq!(store.epoch(b), Some(epoch_b), "b is untouched");
store.remove(a);
assert_eq!(store.epoch(a), None, "removed fragments have no epoch");
}
#[test]
fn store_ids_are_unique() {
let a = FragmentStore::new();
let b = FragmentStore::new();
assert_ne!(a.store_id(), b.store_id());
}
#[test]
fn mutation_counter_advances_on_every_kind_of_change() {
let mut store = FragmentStore::new();
let start = store.mutation_counter();
let a = store.insert(sample_fragment());
assert!(store.mutation_counter() > start, "insert counts");
let after_insert = store.mutation_counter();
let _ = store.get(a);
assert_eq!(store.mutation_counter(), after_insert, "reads do not count");
let _ = store.get_mut(a);
assert!(store.mutation_counter() > after_insert, "get_mut counts");
let after_mut = store.mutation_counter();
store.remove(a);
assert!(store.mutation_counter() > after_mut, "remove counts");
}