Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: CI

on:
push:
branches:
- main
pull_request:
branches:
- main

env:
CARGO_TERM_COLOR: always

jobs:
default-build:
name: Test
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
# os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: dtolnay/rust-toolchain@stable
- uses: actions/checkout@v5
with:
lfs: true
- run: rustup component add rustfmt clippy

# dependencies for wxWidgets (see installation instructions and github
# workflow of https://github.com/allendang/wxdragon)
- name: Install deps
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libgtk-3-dev libpng-dev libjpeg-dev libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev libexpat1-dev libtiff-dev libwebkit2gtk-4.1-dev libxtst-dev

- name: Cargo cache
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

- name: build
run: cargo build

- name: clippy
run: cargo clippy --all-features -- -D warnings

- name: check formatting
run: cargo fmt --all -- --check

- name: headless tests
if: runner.os == 'Linux'
uses: coactions/setup-xvfb@v1
with:
# skip main doc test because it opens a window
# run: cargo test --all-features --verbose
run: cargo test --all-features --verbose --lib --tests

- name: headless tests
if: runner.os == 'Windows'
# skip main doc test because it opens a window
# run: cargo test --all-features --verbose
run: cargo test --all-features --verbose --lib --tests

- name: upload test outputs
uses: actions/upload-artifact@v4
if: always()
with:
name: tests-outputs
path: |
tests/outputs/
retention-days: 7

docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

# dependencies for wxWidgets (see installation instructions and github
# workflow of https://github.com/allendang/wxdragon)
- name: Install deps
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libgtk-3-dev libpng-dev libjpeg-dev libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev libexpat1-dev libtiff-dev libwebkit2gtk-4.1-dev libxtst-dev

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Build documentation
run: cargo doc --all-features --no-deps
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.aider*
target
/tests/outputs
24 changes: 11 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,22 +98,20 @@
//! }
//! }
//!
//! fn main() {
//! let _ = wxdragon::main(|_| {
//! let frame = wx::Frame::builder()
//! .with_title("Getting started")
//! // with this, wx produces a canvas of size 800 x 600
//! .with_size(wx::Size::new(852, 689))
//! .build();
//! let _ = wxdragon::main(|_| {
//! let frame = wx::Frame::builder()
//! .with_title("Getting started")
//! // with this, wx produces a canvas of size 800 x 600
//! .with_size(wx::Size::new(852, 689))
//! .build();
//!
//! let drawing_panel = DrawingPanel::new(&frame);
//! let drawing_panel = DrawingPanel::new(&frame);
//!
//! // Initial paint
//! drawing_panel.refresh(true, None);
//! // Initial paint
//! drawing_panel.refresh(true, None);
//!
//! frame.show(true);
//! });
//! }
//! frame.show(true);
//! });
//! ```
//!
//! You can find more details in the examples for how to integrate a plot in
Expand Down
2 changes: 1 addition & 1 deletion tests/3d_plot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use test_utils::run_plotters_image_test;

#[test]
fn test_3d_plot() -> anyhow::Result<()> {
run_plotters_image_test(1024, 768, "tests/3d_plot", draw_3d_plot)
run_plotters_image_test(1024, 768, "tests/3d_plot.png", draw_3d_plot)
}

fn draw_3d_plot<C: DeviceContext>(backend: WxBackend<C>) -> anyhow::Result<()> {
Expand Down
2 changes: 1 addition & 1 deletion tests/chart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use test_utils::run_plotters_image_test;

#[test]
fn test_chart() -> anyhow::Result<()> {
run_plotters_image_test(1024, 768, "tests/chart", draw_chart)
run_plotters_image_test(1024, 768, "tests/chart.png", draw_chart)
}

fn draw_chart<C: DeviceContext>(backend: WxBackend<C>) -> anyhow::Result<()> {
Expand Down
7 changes: 6 additions & 1 deletion tests/full_palette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ use test_utils::run_plotters_image_test;

#[test]
fn test_full_palette() -> anyhow::Result<()> {
run_plotters_image_test(2000, 850, "tests/full_palette", draw_full_palette)
run_plotters_image_test(
2000,
850,
"tests/full_palette.png",
draw_full_palette,
)
}
fn draw_full_palette<C: DeviceContext>(
backend: WxBackend<C>,
Expand Down
2 changes: 1 addition & 1 deletion tests/mandelbrot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use test_utils::run_plotters_image_test;

#[test]
fn test_mandelbrot() -> Result<()> {
run_plotters_image_test(800, 600, "tests/mandelbrot", draw_mandelbrot)
run_plotters_image_test(800, 600, "tests/mandelbrot.png", draw_mandelbrot)
}

fn draw_mandelbrot<C: DeviceContext>(backend: WxBackend<C>) -> Result<()> {
Expand Down
56 changes: 42 additions & 14 deletions tests/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

use std::fs;
use std::io;
use std::path::Path;
use std::path::PathBuf;
use std::process;

use anyhow::{Context, Result};
Expand All @@ -14,15 +16,18 @@ use wxdragon::{self as wx};
///
/// This function sets up a wxWidgets `MemoryDC`, draws on it using the
/// provided `draw_fn`, then compares the resulting bitmap with a reference
/// image loaded from `{path_root}.png`. If the images do not match, the test
/// image loaded from `{reference_png}`. If the images do not match, the test
/// will fail.
///
/// In case of image mismatch, the actual producted image i saved in
/// subdirectory `/outputs/` of the directory containing `{reference_png}`.
///
/// # Arguments
///
/// * `width`: width of the drawing area.
/// * `height`: height of the drawing area.
/// * `path_root`: used to build the path `"{path_root}.png"` to the reference
/// PNG image for non-regression comparison.
/// * `reference_png`: path to the reference PNG image for non-regression
/// comparison.
/// * `draw_fn`: closure that performs the drawing operations.
///
/// # Returns
Expand All @@ -33,14 +38,26 @@ use wxdragon::{self as wx};
pub fn run_plotters_image_test<F>(
width: u32,
height: u32,
path_root: &str,
reference_png: impl Into<PathBuf>,
draw_fn: F,
) -> Result<()>
where
F: FnOnce(WxBackend<wx::MemoryDC>) -> Result<()> + Send + 'static,
{
let reference_png = format!("{path_root}.png");
let actual_png = format!("{path_root}_actual.png"); // saved if mismatch
let reference_png = reference_png.into();
let (output_dir, actual_png) = {
let output_dir = reference_png
.parent()
.unwrap_or_else(|| Path::new(""))
.join("outputs");
let actual_png = output_dir.join(
reference_png
.file_name()
.context("Invalid reference_png path: missing filename")?,
);
(output_dir, actual_png)
};

let _ = wx::main(move |_| {
let result = (|| -> Result<()> {
// setup the backend with an empty bitmap
Expand Down Expand Up @@ -69,25 +86,36 @@ where
let expected = image::load(
io::BufReader::new(
fs::File::open(&reference_png).with_context(|| {
format!("failed to open {reference_png}")
format!("failed to open {}", reference_png.display())
})?,
),
image::ImageFormat::Png,
)
.with_context(|| "failed to load {reference_png}")?;
.with_context(|| {
format!("failed to load {}", reference_png.display())
})?;

// save actual image for later comparison in case of failure
fs::create_dir_all(&output_dir).context(format!(
"failed to create directory {}",
output_dir.display()
))?;
image
.save(&actual_png)
.context(format!("failed to save {}", actual_png.display()))?;

if expected == image::DynamicImage::ImageRgba8(image.clone()) {
Ok(())
} else {
image
.save(&actual_png)
.context("failed to save {actual_png}")?;
let message = format!(
"ERROR: image mismatch.
Compare the following two files manually, then \
update the reference image if needed.
reference image: {reference_png}
actual image : {actual_png}
"
reference image: {}
actual image : {}
",
reference_png.display(),
actual_png.display()
);
anyhow::bail!(message)
}
Expand Down
Loading