• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
check_color(left: BackendColor, right: RGBAColor)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 {
new(width: u32, height: u32) -> Self56     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 
check_before_draw(&mut self)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 {
fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result106     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 
get_size(&self) -> (u32, u32)116     fn get_size(&self) -> (u32, u32) {
117         (self.width, self.height)
118     }
119 
ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<MockedError>>120     fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<MockedError>> {
121         self.init_count += 1;
122         Ok(())
123     }
124 
present(&mut self) -> Result<(), DrawingErrorKind<MockedError>>125     fn present(&mut self) -> Result<(), DrawingErrorKind<MockedError>> {
126         self.init_count = 0;
127         self.draw_count = 0;
128         Ok(())
129     }
130 
draw_pixel( &mut self, point: BackendCoord, color: BackendColor, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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 
draw_line<S: BackendStyle>( &mut self, from: BackendCoord, to: BackendCoord, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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 
draw_rect<S: BackendStyle>( &mut self, upper_left: BackendCoord, bottom_right: BackendCoord, style: &S, fill: bool, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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 
draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( &mut self, path: I, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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 
draw_circle<S: BackendStyle>( &mut self, center: BackendCoord, radius: u32, style: &S, fill: bool, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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 
fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( &mut self, path: I, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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 
draw_text<S: BackendTextStyle>( &mut self, text: &str, style: &S, pos: BackendCoord, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>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 {
drop(&mut self)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 
create_mocked_drawing_area<F: FnOnce(&mut MockedBackend)>( width: u32, height: u32, setup: F, ) -> DrawingArea<MockedBackend, Shift>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