1 | use crate::coord::Shift; |
2 | use crate::drawing::area::IntoDrawingArea; |
3 | use crate::drawing::DrawingArea; |
4 | use crate::style::RGBAColor; |
5 | use plotters_backend::{ |
6 | BackendColor, BackendCoord, BackendStyle, BackendTextStyle, DrawingBackend, DrawingErrorKind, |
7 | }; |
8 | |
9 | use std::collections::VecDeque; |
10 | |
11 | pub fn check_color(left: BackendColor, right: RGBAColor) { |
12 | assert_eq!( |
13 | RGBAColor(left.rgb.0, left.rgb.1, left.rgb.2, left.alpha), |
14 | right |
15 | ); |
16 | } |
17 | |
18 | pub struct MockedBackend { |
19 | height: u32, |
20 | width: u32, |
21 | init_count: u32, |
22 | pub draw_count: u32, |
23 | pub num_draw_pixel_call: u32, |
24 | pub num_draw_line_call: u32, |
25 | pub num_draw_rect_call: u32, |
26 | pub num_draw_circle_call: u32, |
27 | pub num_draw_text_call: u32, |
28 | pub num_draw_path_call: u32, |
29 | pub num_fill_polygon_call: u32, |
30 | check_draw_pixel: VecDeque<Box<dyn FnMut(RGBAColor, BackendCoord)>>, |
31 | check_draw_line: VecDeque<Box<dyn FnMut(RGBAColor, u32, BackendCoord, BackendCoord)>>, |
32 | check_draw_rect: VecDeque<Box<dyn FnMut(RGBAColor, u32, bool, BackendCoord, BackendCoord)>>, |
33 | check_draw_path: VecDeque<Box<dyn FnMut(RGBAColor, u32, Vec<BackendCoord>)>>, |
34 | check_draw_circle: VecDeque<Box<dyn FnMut(RGBAColor, u32, bool, BackendCoord, u32)>>, |
35 | check_draw_text: VecDeque<Box<dyn FnMut(RGBAColor, &str, f64, BackendCoord, &str)>>, |
36 | check_fill_polygon: VecDeque<Box<dyn FnMut(RGBAColor, Vec<BackendCoord>)>>, |
37 | drop_check: Option<Box<dyn FnMut(&Self)>>, |
38 | } |
39 | |
40 | macro_rules! def_set_checker_func { |
41 | (drop_check, $($param:ty),*) => { |
42 | pub fn drop_check<T: FnMut($($param,)*) + 'static>(&mut self, check:T) -> &mut Self { |
43 | self.drop_check = Some(Box::new(check)); |
44 | self |
45 | } |
46 | }; |
47 | ($name:ident, $($param:ty),*) => { |
48 | pub fn $name<T: FnMut($($param,)*) + 'static>(&mut self, check:T) -> &mut Self { |
49 | self.$name.push_back(Box::new(check)); |
50 | self |
51 | } |
52 | } |
53 | } |
54 | |
55 | impl MockedBackend { |
56 | pub fn new(width: u32, height: u32) -> Self { |
57 | MockedBackend { |
58 | height, |
59 | width, |
60 | init_count: 0, |
61 | draw_count: 0, |
62 | num_draw_pixel_call: 0, |
63 | num_draw_line_call: 0, |
64 | num_draw_rect_call: 0, |
65 | num_draw_circle_call: 0, |
66 | num_draw_text_call: 0, |
67 | num_draw_path_call: 0, |
68 | num_fill_polygon_call: 0, |
69 | check_draw_pixel: vec![].into(), |
70 | check_draw_line: vec![].into(), |
71 | check_draw_rect: vec![].into(), |
72 | check_draw_path: vec![].into(), |
73 | check_draw_circle: vec![].into(), |
74 | check_draw_text: vec![].into(), |
75 | check_fill_polygon: vec![].into(), |
76 | drop_check: None, |
77 | } |
78 | } |
79 | |
80 | def_set_checker_func!(check_draw_pixel, RGBAColor, BackendCoord); |
81 | def_set_checker_func!(check_draw_line, RGBAColor, u32, BackendCoord, BackendCoord); |
82 | def_set_checker_func!( |
83 | check_draw_rect, |
84 | RGBAColor, |
85 | u32, |
86 | bool, |
87 | BackendCoord, |
88 | BackendCoord |
89 | ); |
90 | def_set_checker_func!(check_draw_path, RGBAColor, u32, Vec<BackendCoord>); |
91 | def_set_checker_func!(check_draw_circle, RGBAColor, u32, bool, BackendCoord, u32); |
92 | def_set_checker_func!(check_draw_text, RGBAColor, &str, f64, BackendCoord, &str); |
93 | def_set_checker_func!(drop_check, &Self); |
94 | def_set_checker_func!(check_fill_polygon, RGBAColor, Vec<BackendCoord>); |
95 | |
96 | fn check_before_draw(&mut self) { |
97 | self.draw_count += 1; |
98 | //assert_eq!(self.init_count, self.draw_count); |
99 | } |
100 | } |
101 | |
102 | #[derive(Debug)] |
103 | pub struct MockedError; |
104 | |
105 | impl std::fmt::Display for MockedError { |
106 | fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
107 | write!(fmt, "MockedError" ) |
108 | } |
109 | } |
110 | |
111 | impl std::error::Error for MockedError {} |
112 | |
113 | impl DrawingBackend for MockedBackend { |
114 | type ErrorType = MockedError; |
115 | |
116 | fn get_size(&self) -> (u32, u32) { |
117 | (self.width, self.height) |
118 | } |
119 | |
120 | fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<MockedError>> { |
121 | self.init_count += 1; |
122 | Ok(()) |
123 | } |
124 | |
125 | fn present(&mut self) -> Result<(), DrawingErrorKind<MockedError>> { |
126 | self.init_count = 0; |
127 | self.draw_count = 0; |
128 | Ok(()) |
129 | } |
130 | |
131 | fn draw_pixel( |
132 | &mut self, |
133 | point: BackendCoord, |
134 | color: BackendColor, |
135 | ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { |
136 | self.check_before_draw(); |
137 | self.num_draw_pixel_call += 1; |
138 | let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha); |
139 | if let Some(mut checker) = self.check_draw_pixel.pop_front() { |
140 | checker(color, point); |
141 | |
142 | if self.check_draw_pixel.is_empty() { |
143 | self.check_draw_pixel.push_back(checker); |
144 | } |
145 | } |
146 | Ok(()) |
147 | } |
148 | |
149 | fn draw_line<S: BackendStyle>( |
150 | &mut self, |
151 | from: BackendCoord, |
152 | to: BackendCoord, |
153 | style: &S, |
154 | ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { |
155 | self.check_before_draw(); |
156 | self.num_draw_line_call += 1; |
157 | let color = style.color(); |
158 | let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha); |
159 | if let Some(mut checker) = self.check_draw_line.pop_front() { |
160 | checker(color, style.stroke_width(), from, to); |
161 | |
162 | if self.check_draw_line.is_empty() { |
163 | self.check_draw_line.push_back(checker); |
164 | } |
165 | } |
166 | Ok(()) |
167 | } |
168 | |
169 | fn draw_rect<S: BackendStyle>( |
170 | &mut self, |
171 | upper_left: BackendCoord, |
172 | bottom_right: BackendCoord, |
173 | style: &S, |
174 | fill: bool, |
175 | ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { |
176 | self.check_before_draw(); |
177 | self.num_draw_rect_call += 1; |
178 | let color = style.color(); |
179 | let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha); |
180 | if let Some(mut checker) = self.check_draw_rect.pop_front() { |
181 | checker(color, style.stroke_width(), fill, upper_left, bottom_right); |
182 | |
183 | if self.check_draw_rect.is_empty() { |
184 | self.check_draw_rect.push_back(checker); |
185 | } |
186 | } |
187 | Ok(()) |
188 | } |
189 | |
190 | fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( |
191 | &mut self, |
192 | path: I, |
193 | style: &S, |
194 | ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { |
195 | self.check_before_draw(); |
196 | self.num_draw_path_call += 1; |
197 | let color = style.color(); |
198 | let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha); |
199 | if let Some(mut checker) = self.check_draw_path.pop_front() { |
200 | checker(color, style.stroke_width(), path.into_iter().collect()); |
201 | |
202 | if self.check_draw_path.is_empty() { |
203 | self.check_draw_path.push_back(checker); |
204 | } |
205 | } |
206 | Ok(()) |
207 | } |
208 | |
209 | fn draw_circle<S: BackendStyle>( |
210 | &mut self, |
211 | center: BackendCoord, |
212 | radius: u32, |
213 | style: &S, |
214 | fill: bool, |
215 | ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { |
216 | self.check_before_draw(); |
217 | self.num_draw_circle_call += 1; |
218 | let color = style.color(); |
219 | let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha); |
220 | if let Some(mut checker) = self.check_draw_circle.pop_front() { |
221 | checker(color, style.stroke_width(), fill, center, radius); |
222 | |
223 | if self.check_draw_circle.is_empty() { |
224 | self.check_draw_circle.push_back(checker); |
225 | } |
226 | } |
227 | Ok(()) |
228 | } |
229 | |
230 | fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( |
231 | &mut self, |
232 | path: I, |
233 | style: &S, |
234 | ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { |
235 | self.check_before_draw(); |
236 | self.num_fill_polygon_call += 1; |
237 | let color = style.color(); |
238 | let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha); |
239 | if let Some(mut checker) = self.check_fill_polygon.pop_front() { |
240 | checker(color, path.into_iter().collect()); |
241 | |
242 | if self.check_fill_polygon.is_empty() { |
243 | self.check_fill_polygon.push_back(checker); |
244 | } |
245 | } |
246 | Ok(()) |
247 | } |
248 | |
249 | fn draw_text<S: BackendTextStyle>( |
250 | &mut self, |
251 | text: &str, |
252 | style: &S, |
253 | pos: BackendCoord, |
254 | ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { |
255 | let color = style.color(); |
256 | let color = RGBAColor(color.rgb.0, color.rgb.1, color.rgb.2, color.alpha); |
257 | self.check_before_draw(); |
258 | self.num_draw_text_call += 1; |
259 | if let Some(mut checker) = self.check_draw_text.pop_front() { |
260 | checker(color, style.family().as_str(), style.size(), pos, text); |
261 | |
262 | if self.check_draw_text.is_empty() { |
263 | self.check_draw_text.push_back(checker); |
264 | } |
265 | } |
266 | Ok(()) |
267 | } |
268 | } |
269 | |
270 | impl Drop for MockedBackend { |
271 | fn drop(&mut self) { |
272 | // `self.drop_check` is typically a testing function; it can panic. |
273 | // The current `drop` call may be a part of stack unwinding caused |
274 | // by another panic. If so, we should never call it. |
275 | if std::thread::panicking() { |
276 | return; |
277 | } |
278 | |
279 | let mut temp = None; |
280 | std::mem::swap(&mut temp, &mut self.drop_check); |
281 | |
282 | if let Some(mut checker) = temp { |
283 | checker(self); |
284 | } |
285 | } |
286 | } |
287 | |
288 | pub fn create_mocked_drawing_area<F: FnOnce(&mut MockedBackend)>( |
289 | width: u32, |
290 | height: u32, |
291 | setup: F, |
292 | ) -> DrawingArea<MockedBackend, Shift> { |
293 | let mut backend = MockedBackend::new(width, height); |
294 | setup(&mut backend); |
295 | backend.into_drawing_area() |
296 | } |
297 | |