| 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 | |