Skip to main content

laser_pdf/test_utils/
record_passes.rs

1use std::cell::RefCell;
2
3use crate::*;
4
5#[derive(PartialEq, Debug)]
6pub struct Break {
7    pub page: usize,
8    pub layer: usize,
9    pub pos: (f32, f32),
10}
11
12#[derive(PartialEq, Debug)]
13pub struct BreakableDraw {
14    pub full_height: f32,
15    pub preferred_height_break_count: u32,
16    pub breaks: Vec<Break>,
17}
18
19#[derive(PartialEq, Debug)]
20pub enum Pass {
21    FirstLocationUsage {
22        width: WidthConstraint,
23        first_height: f32,
24        full_height: f32,
25    },
26    Measure {
27        width: WidthConstraint,
28        first_height: f32,
29
30        /// Some implies a breakable context.
31        full_height: Option<f32>,
32    },
33    Draw(DrawPass),
34}
35
36#[derive(PartialEq, Debug)]
37pub struct DrawPass {
38    pub width: WidthConstraint,
39    pub first_height: f32,
40    pub preferred_height: Option<f32>,
41    pub page: usize,
42    pub layer: usize,
43    pub pos: (f32, f32),
44    pub breakable: Option<BreakableDraw>,
45}
46
47/// This element can be used to test the methods that get called on an element. This is mostly
48/// useful for testing containers. It also asserts that the two mutable references passed to a
49/// breakable measure start at zero.
50pub struct RecordPasses<E: Element> {
51    element: E,
52    passes: RefCell<Vec<Pass>>,
53}
54
55impl<E: Element> RecordPasses<E> {
56    pub fn new(element: E) -> Self {
57        RecordPasses {
58            element,
59            passes: RefCell::new(Vec::new()),
60        }
61    }
62
63    pub fn into_passes(self) -> Vec<Pass> {
64        self.passes.into_inner()
65    }
66
67    pub fn assert_draw(&self, pass: DrawPass) {
68        self.assert_draws(&[pass]);
69    }
70
71    pub fn assert_draws(&self, expected_draws: &[DrawPass]) {
72        let passes = self.passes.borrow();
73        let actual_draws: Vec<_> = passes
74            .iter()
75            .filter_map(|p| if let Pass::Draw(d) = p { Some(d) } else { None })
76            .collect();
77
78        assert!(
79            expected_draws.iter().eq(actual_draws.iter().map(|d| *d)),
80            "assertion `actual_draws == expected_draws` failed\nactual_draws: {:#?}\nexpected_draws: {:#?}",
81            actual_draws,
82            expected_draws,
83        );
84    }
85
86    pub fn assert_draw_count(&self, count: usize) {
87        let passes = self.passes.borrow();
88        let draw_passes = passes.iter().filter(|p| matches!(p, Pass::Draw { .. }));
89        assert_eq!(draw_passes.count(), count);
90    }
91
92    pub fn assert_measure_count(&self, count: usize) {
93        let passes = self.passes.borrow();
94        let measure_passes = passes.iter().filter(|p| matches!(p, Pass::Measure { .. }));
95        assert_eq!(measure_passes.count(), count);
96    }
97
98    pub fn assert_first_location_usage_count(&self, count: usize) {
99        let passes = self.passes.borrow();
100        let first_location_usage_passes = passes
101            .iter()
102            .filter(|p| matches!(p, Pass::FirstLocationUsage { .. }));
103        assert_eq!(first_location_usage_passes.count(), count);
104    }
105}
106
107impl<E: Element> Element for RecordPasses<E> {
108    fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
109        self.passes.borrow_mut().push(Pass::FirstLocationUsage {
110            width: ctx.width,
111            first_height: ctx.first_height,
112            full_height: ctx.full_height,
113        });
114
115        self.element.first_location_usage(ctx)
116    }
117
118    fn measure(&self, ctx: MeasureCtx) -> ElementSize {
119        if let Some(ref b) = ctx.breakable {
120            assert_eq!(*b.break_count, 0);
121            assert_eq!(*b.extra_location_min_height, None);
122        }
123
124        self.passes.borrow_mut().push(Pass::Measure {
125            width: ctx.width,
126            first_height: ctx.first_height,
127            full_height: ctx.breakable.as_ref().map(|b| b.full_height),
128        });
129
130        self.element.measure(ctx)
131    }
132
133    fn draw(&self, ctx: DrawCtx) -> ElementSize {
134        let width = ctx.width;
135        let first_height = ctx.first_height;
136        let preferred_height = ctx.preferred_height;
137
138        let page = ctx.location.page_idx;
139        let layer = ctx.location.layer_idx;
140        let pos = ctx.location.pos;
141
142        let result;
143
144        let breakable = if let Some(breakable) = ctx.breakable {
145            let full_height = breakable.full_height;
146            let preferred_height_break_count = breakable.preferred_height_break_count;
147
148            let mut breaks = Vec::new();
149
150            result = self.element.draw(DrawCtx {
151                breakable: Some(crate::BreakableDraw {
152                    do_break: &mut |pdf, location_idx, height| {
153                        let location = (breakable.do_break)(pdf, location_idx, height);
154
155                        breaks.push(Break {
156                            page: location.page_idx,
157                            layer: location.layer_idx,
158                            pos: location.pos,
159                        });
160
161                        location
162                    },
163                    ..breakable
164                }),
165                ..ctx
166            });
167
168            Some(BreakableDraw {
169                full_height,
170                preferred_height_break_count,
171                breaks,
172            })
173        } else {
174            result = self.element.draw(ctx);
175            None
176        };
177
178        self.passes.borrow_mut().push(Pass::Draw(DrawPass {
179            width,
180            first_height,
181            preferred_height,
182            page,
183            layer,
184            pos,
185            breakable,
186        }));
187
188        result
189    }
190}