diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..1b8e2ec --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,26 @@ +name: Run Tests +on: + push: {} + pull_request: {} + +jobs: + test: + timeout-minutes: 30 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: install native dependencies + run: sudo apt-get update; sudo apt-get install libgtk-3-dev + + - name: Run tests + run: cargo test --package plotters-piet + # For now we skip the plotters-druid tests because the latest druid + # version currently depends on a pre-release version of piet. + + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: images + path: ./**/tests/**/*.png diff --git a/plotters-piet/Cargo.toml b/plotters-piet/Cargo.toml index aaffbcd..9ffde60 100644 --- a/plotters-piet/Cargo.toml +++ b/plotters-piet/Cargo.toml @@ -21,3 +21,4 @@ plotters-backend = "^0.3" [dev-dependencies] plotters = "^0.3" piet-common = {version = "0.6.1", features = ["png"]} +image = { version = "0.24.9", default-features = false, features = ["png"] } diff --git a/plotters-piet/tests/snowflake.png b/plotters-piet/tests/snowflake.png new file mode 100644 index 0000000..1cf8288 Binary files /dev/null and b/plotters-piet/tests/snowflake.png differ diff --git a/plotters-piet/tests/snowflake.rs b/plotters-piet/tests/snowflake.rs new file mode 100644 index 0000000..b48f464 --- /dev/null +++ b/plotters-piet/tests/snowflake.rs @@ -0,0 +1,97 @@ +use image::RgbaImage; +use piet_common::{BitmapTarget, Device, ImageFormat}; +use plotters::prelude::*; +use plotters_piet::PietBackend; + +// Copied from the plotters "Snowflake" example: +// https://github.com/plotters-rs/plotters/blob/0f195eadaac7d9a2390a3707fbe192f8e2645d34/plotters/examples/snowflake.rs + +#[test] +fn snapshot_test() { + let width = 600; + let height = 450; + + let mut device = Device::new().unwrap(); + let mut bitmap = device.bitmap_target(width, height, 1.0).unwrap(); + + { + let mut render_ctx = bitmap.render_context(); + let piet_backend = PietBackend { + size: (width as u32, height as u32), + render_ctx: &mut render_ctx, + }; + + let root = piet_backend.into_drawing_area(); + + root.fill(&WHITE).unwrap(); + + let mut chart = ChartBuilder::on(&root) + .build_cartesian_2d(-2.0..2.0, -1.5..1.5) + .unwrap(); + + let mut snowflake_vertices = { + let mut current: Vec<(f64, f64)> = vec![ + (0.0, 1.0), + ((3.0f64).sqrt() / 2.0, -0.5), + (-(3.0f64).sqrt() / 2.0, -0.5), + ]; + for _ in 0..6 { + current = snowflake_iter(¤t[..]); + } + current + }; + + chart + .draw_series(std::iter::once(Polygon::new( + snowflake_vertices.clone(), + RED.mix(0.2), + ))) + .unwrap(); + snowflake_vertices.push(snowflake_vertices[0]); + chart + .draw_series(std::iter::once(PathElement::new(snowflake_vertices, RED))) + .unwrap(); + + root.present().unwrap(); + } + + let expected = image::load_from_memory(include_bytes!("snowflake.png")) + .expect("cannot decode snapshot") + .into_rgba8(); + + let actual = bitmap_to_image(&mut bitmap); + + if expected != actual { + actual.save("tests/snowflake-actual.png").unwrap(); + // assert_eq would spam the console with the entire list of bytes on fails + assert!(false, "images differ"); + } +} + +fn snowflake_iter(points: &[(f64, f64)]) -> Vec<(f64, f64)> { + let mut ret = vec![]; + for i in 0..points.len() { + let (start, end) = (points[i], points[(i + 1) % points.len()]); + let t = ((end.0 - start.0) / 3.0, (end.1 - start.1) / 3.0); + let s = ( + t.0 * 0.5 - t.1 * (0.75f64).sqrt(), + t.1 * 0.5 + (0.75f64).sqrt() * t.0, + ); + ret.push(start); + ret.push((start.0 + t.0, start.1 + t.1)); + ret.push((start.0 + t.0 + s.0, start.1 + t.1 + s.1)); + ret.push((start.0 + t.0 * 2.0, start.1 + t.1 * 2.0)); + } + ret +} + +fn bitmap_to_image(bitmap: &mut BitmapTarget) -> RgbaImage { + let buffer = bitmap.to_image_buf(ImageFormat::RgbaPremul).unwrap(); + + RgbaImage::from_raw( + buffer.width() as u32, + buffer.height() as u32, + buffer.raw_pixels().to_vec(), + ) + .unwrap() +}