Skip to main content

laser_pdf/test_utils/
assert_passes.rs

1use std::cell::Cell;
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 {
34        width: WidthConstraint,
35        first_height: f32,
36        preferred_height: Option<f32>,
37        page: usize,
38        layer: usize,
39        pos: (f32, f32),
40        breakable: Option<BreakableDraw>,
41    },
42}
43
44/// This element can be used to test the methods that get called on an element. This is mostly
45/// useful for testing containers. It also asserts that the two mutable references passed to a
46/// breakable measure start at zero.
47pub struct AssertPasses<E: Element> {
48    element: E,
49    passes: Vec<Pass>,
50    current: Cell<usize>,
51}
52
53impl<E: Element> AssertPasses<E> {
54    pub fn new(element: E, passes: Vec<Pass>) -> Self {
55        AssertPasses {
56            element,
57            passes,
58            current: Cell::new(0),
59        }
60    }
61}
62
63impl<E: Element> Drop for AssertPasses<E> {
64    fn drop(&mut self) {
65        // If this throws, make sure there isn't another error above, because this also gets dropped
66        // while unwinding the stack.
67        //
68        // TODO: Maybe we can check if an unwind is already in progress here?
69        assert_eq!(self.current.get(), self.passes.len());
70    }
71}
72
73impl<E: Element> Element for AssertPasses<E> {
74    fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
75        let idx = self.current.get();
76        self.current.set(idx + 1);
77
78        let current = &self.passes[idx];
79
80        assert_eq!(
81            &Pass::FirstLocationUsage {
82                width: ctx.width,
83                first_height: ctx.first_height,
84                full_height: ctx.full_height,
85            },
86            current,
87        );
88
89        self.element.first_location_usage(ctx)
90    }
91
92    fn measure(&self, ctx: MeasureCtx) -> ElementSize {
93        let idx = self.current.get();
94        self.current.set(idx + 1);
95
96        let current = &self.passes[idx];
97
98        if let Some(ref b) = ctx.breakable {
99            assert_eq!(*b.break_count, 0);
100            assert_eq!(*b.extra_location_min_height, None);
101        }
102
103        assert_eq!(
104            &Pass::Measure {
105                width: ctx.width,
106                first_height: ctx.first_height,
107                full_height: ctx.breakable.as_ref().map(|b| b.full_height),
108            },
109            current,
110        );
111
112        self.element.measure(ctx)
113    }
114
115    fn draw(&self, ctx: DrawCtx) -> ElementSize {
116        let idx = self.current.get();
117        self.current.set(idx + 1);
118
119        let current = &self.passes[idx];
120
121        let width = ctx.width;
122        let first_height = ctx.first_height;
123        let preferred_height = ctx.preferred_height;
124
125        let page = ctx.location.page_idx;
126        let layer = ctx.location.layer_idx;
127        let pos = ctx.location.pos;
128
129        let result;
130
131        let breakable = if let Some(breakable) = ctx.breakable {
132            let full_height = breakable.full_height;
133            let preferred_height_break_count = breakable.preferred_height_break_count;
134
135            let mut breaks = Vec::new();
136
137            result = self.element.draw(DrawCtx {
138                breakable: Some(crate::BreakableDraw {
139                    do_break: &mut |pdf, location_idx, height| {
140                        let location = (breakable.do_break)(pdf, location_idx, height);
141
142                        breaks.push(Break {
143                            page: location.page_idx,
144                            layer: location.layer_idx,
145                            pos: location.pos,
146                        });
147
148                        location
149                    },
150                    ..breakable
151                }),
152                ..ctx
153            });
154
155            Some(BreakableDraw {
156                full_height,
157                preferred_height_break_count,
158                breaks,
159            })
160        } else {
161            result = self.element.draw(ctx);
162            None
163        };
164
165        assert_eq!(
166            &Pass::Draw {
167                width,
168                first_height,
169                preferred_height,
170                page,
171                layer,
172                pos,
173                breakable,
174            },
175            current,
176        );
177
178        result
179    }
180}