tests.rs raw

use super::*;
use crate::display_list::{Fragment, Glyph, GlyphRun};
use crate::geom::{Affine, Rect, Stroke};
use crate::paint::{Blob, Color, FontData};

fn font() -> FontData {
    // The bytes are never parsed here: `ink_bounds` reads glyph positions and
    // the run's size, both of which the display list already carries.
    FontData::new(Blob::new(std::sync::Arc::new(Vec::new())), 0)
}

fn run(size: f32, positions: &[(f32, f32)]) -> GlyphRun {
    GlyphRun {
        font: font(),
        size,
        brush: Color::BLACK.into(),
        glyphs: positions
            .iter()
            .map(|&(x, y)| Glyph { id: 0, x, y })
            .collect(),
        normalized_coords: Vec::new(),
        hint: true,
        synthesis: Default::default(),
    }
}

#[test]
fn a_run_bounds_its_glyphs_with_room_for_their_ink() {
    let bounds = run(10.0, &[(100.0, 200.0), (140.0, 200.0)])
        .ink_bounds()
        .expect("a run with glyphs has bounds");
    // The placements alone span (100,200)–(140,200); the reach is what turns
    // that into a box the outlines cannot escape.
    assert!(bounds.x0 <= 100.0 && bounds.x1 >= 140.0);
    assert!(bounds.y0 < 200.0 && bounds.y1 > 200.0);
    assert!(bounds.x1 - 140.0 >= 10.0, "an em of advance at the least");
}

#[test]
fn an_empty_run_has_no_bounds() {
    assert!(run(10.0, &[]).ink_bounds().is_none());
}

#[test]
fn scope_markers_and_children_report_no_bounds() {
    let mut fragment = Fragment::new();
    fragment.push_clip(Rect::new(0.0, 0.0, 10.0, 10.0));
    fragment.push_transform(Affine::translate((1.0, 1.0)));
    fragment.pop_transform();
    fragment.pop_clip();
    for item in &fragment.items {
        assert_eq!(item.draw_bounds(Affine::IDENTITY), None);
    }
}

#[test]
fn a_fill_reports_its_shape_under_the_transform() {
    let mut fragment = Fragment::new();
    fragment.fill(Rect::new(0.0, 0.0, 10.0, 10.0), Color::BLACK);
    let bounds = fragment.items[0]
        .draw_bounds(Affine::translate((100.0, 0.0)))
        .expect("a fill draws");
    assert_eq!(bounds, Rect::new(100.0, 0.0, 110.0, 10.0));
}

#[test]
fn a_stroke_reports_past_its_shape() {
    let mut fragment = Fragment::new();
    fragment.stroke(
        Rect::new(0.0, 0.0, 10.0, 10.0),
        Color::BLACK,
        Stroke::new(4.0),
    );
    let bounds = fragment.items[0]
        .draw_bounds(Affine::IDENTITY)
        .expect("a stroke draws");
    assert!(bounds.x0 <= -2.0 && bounds.x1 >= 12.0);
}

#[test]
fn an_unclipped_stack_keeps_everything() {
    let clip = ClipBounds::new(None);
    assert!(clip.any_visible());
    assert!(clip.visible(Rect::new(-1e9, -1e9, -1e8, -1e8)));
}

#[test]
fn a_clip_culls_what_lies_outside_it() {
    let clip = ClipBounds::new(Some(Rect::new(0.0, 0.0, 100.0, 100.0)));
    assert!(clip.visible(Rect::new(50.0, 50.0, 150.0, 150.0)));
    assert!(!clip.visible(Rect::new(200.0, 0.0, 300.0, 100.0)));
    // Sharing an edge covers no pixels.
    assert!(!clip.visible(Rect::new(100.0, 0.0, 200.0, 100.0)));
}

#[test]
fn nested_clips_intersect_and_unwind() {
    let mut clip = ClipBounds::new(Some(Rect::new(0.0, 0.0, 100.0, 100.0)));
    clip.push(Rect::new(50.0, 0.0, 200.0, 100.0));
    assert_eq!(clip.current(), Some(Rect::new(50.0, 0.0, 100.0, 100.0)));
    assert!(!clip.visible(Rect::new(0.0, 0.0, 40.0, 40.0)));
    clip.pop();
    assert!(clip.visible(Rect::new(0.0, 0.0, 40.0, 40.0)));
}

#[test]
fn a_clip_closed_to_nothing_stays_closed() {
    let mut clip = ClipBounds::new(Some(Rect::new(0.0, 0.0, 100.0, 100.0)));
    clip.push(Rect::new(500.0, 500.0, 600.0, 600.0));
    assert!(!clip.any_visible());
    // A scope opened inside it cannot bring anything back.
    clip.push(Rect::new(0.0, 0.0, 100.0, 100.0));
    assert!(!clip.any_visible());
    assert!(!clip.visible(Rect::new(0.0, 0.0, 100.0, 100.0)));
    clip.pop();
    clip.pop();
    assert!(clip.any_visible());
}

#[test]
fn an_unclipped_outer_scope_is_restored_as_unclipped() {
    let mut clip = ClipBounds::new(None);
    clip.push(Rect::new(0.0, 0.0, 10.0, 10.0));
    assert!(!clip.visible(Rect::new(100.0, 100.0, 200.0, 200.0)));
    clip.pop();
    assert_eq!(clip.current(), None);
    assert!(clip.visible(Rect::new(100.0, 100.0, 200.0, 200.0)));
}

#[test]
fn the_transform_is_what_places_an_item_against_the_clip() {
    let clip = ClipBounds::new(Some(Rect::new(0.0, 0.0, 100.0, 100.0)));
    let mut fragment = Fragment::new();
    fragment.fill(Rect::new(0.0, 0.0, 10.0, 10.0), Color::BLACK);
    let item = &fragment.items[0];
    assert!(clip.item_visible(item, Affine::IDENTITY));
    assert!(!clip.item_visible(item, Affine::translate((1000.0, 0.0))));
}