From 802cf79b25569132b9c1f05d062760dee47ced2c Mon Sep 17 00:00:00 2001 From: threefold3 Date: Sun, 1 Mar 2026 21:36:53 +0100 Subject: [PATCH 01/13] ci: create github workflow to test on Ubuntu, Windows, Mac OS --- .github/workflows/ci.yml | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f866a93 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,64 @@ +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, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: dtolnay/rust-toolchain@stable + - uses: actions/checkout@v5 + - 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: tests + run: cargo test --all-features --verbose + + - name: clippy + run: cargo clippy --all-features -- -D warnings + + - name: check formatting + run: cargo fmt --all -- --check + + docs: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Build documentation + run: cargo doc --all-features --no-deps From f21cdf4745c07ac2f3652eb793045ba2494ffc7f Mon Sep 17 00:00:00 2001 From: threefold3 Date: Sun, 1 Mar 2026 22:14:01 +0100 Subject: [PATCH 02/13] ci: add dependencies for wxwidgets in docs job --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f866a93..cb19165 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,12 @@ jobs: 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 From 1d90e244e58b0cfcfeb70e38c831afb22508646f Mon Sep 17 00:00:00 2001 From: threefold3 Date: Mon, 2 Mar 2026 21:26:21 +0100 Subject: [PATCH 03/13] ci: wrap `cargo test` in `xvfb-run` to allow headless tests --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb19165..e3c2d72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,10 +25,11 @@ jobs: - run: rustup component add rustfmt clippy # dependencies for wxWidgets (see installation instructions and github - # workflow of https://github.com/allendang/wxdragon) + # workflow of https://github.com/allendang/wxdragon); only xvfb is + # specific to this setup - 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 + 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 xvfb - name: Cargo cache uses: actions/cache@v4 @@ -43,7 +44,7 @@ jobs: run: cargo build - name: tests - run: cargo test --all-features --verbose + run: xvfb-run cargo test --all-features --verbose - name: clippy run: cargo clippy --all-features -- -D warnings From 54f5ec8893e41032c6e5817ac2862973cb6802ef Mon Sep 17 00:00:00 2001 From: threefold3 Date: Mon, 2 Mar 2026 21:30:58 +0100 Subject: [PATCH 04/13] ci: skip main doc test because it opens a window --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3c2d72..057d0a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,9 @@ jobs: - name: build run: cargo build + # skip main doc test because it opens a window + - run: sed -i 's/```rust/```no_run/g' src/lib.rs + - name: tests run: xvfb-run cargo test --all-features --verbose From 05cce2e9e2d4b1f1638af712010ec5d9b9906d18 Mon Sep 17 00:00:00 2001 From: threefold3 Date: Mon, 2 Mar 2026 21:53:43 +0100 Subject: [PATCH 05/13] ci: activate git lfs on checkout action --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 057d0a0..7a3448d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,8 @@ jobs: 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 From 12a15fa276b78532125bef5c683d6f064d8614e3 Mon Sep 17 00:00:00 2001 From: threefold3 Date: Mon, 2 Mar 2026 21:56:40 +0100 Subject: [PATCH 06/13] ci: replace xvfb-run by setup-xvfb github action --- .github/workflows/ci.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a3448d..0b2c9b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,11 +27,10 @@ jobs: - run: rustup component add rustfmt clippy # dependencies for wxWidgets (see installation instructions and github - # workflow of https://github.com/allendang/wxdragon); only xvfb is - # specific to this setup + # 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 xvfb + 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 @@ -48,15 +47,17 @@ jobs: # skip main doc test because it opens a window - run: sed -i 's/```rust/```no_run/g' src/lib.rs - - name: tests - run: xvfb-run cargo test --all-features --verbose - - name: clippy run: cargo clippy --all-features -- -D warnings - name: check formatting run: cargo fmt --all -- --check + - name: headless tests + uses: coactions/setup-xvfb@v1 + with: + run: cargo test --all-features --verbose + docs: name: Documentation runs-on: ubuntu-latest From 27e0efe6c705f07d52287428341e2cabb5818cf9 Mon Sep 17 00:00:00 2001 From: threefold3 Date: Mon, 2 Mar 2026 22:07:34 +0100 Subject: [PATCH 07/13] ci: fix checkout action config --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b2c9b3..37e0935 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: actions/checkout@v5 with: - - lfs: true + lfs: true - run: rustup component add rustfmt clippy # dependencies for wxWidgets (see installation instructions and github From 0c4930fd0227e8f08c4b22f3afd8e2514dbf60f6 Mon Sep 17 00:00:00 2001 From: threefold3 Date: Mon, 2 Mar 2026 22:08:46 +0100 Subject: [PATCH 08/13] ci: temporarily disable macos --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37e0935..db876d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,8 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, windows-latest] + # os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - uses: dtolnay/rust-toolchain@stable From c888116dd4f41800c57e53debd02b53baa0d0129 Mon Sep 17 00:00:00 2001 From: threefold3 Date: Mon, 2 Mar 2026 22:13:34 +0100 Subject: [PATCH 09/13] ci: simplify way to skip doc tests --- .github/workflows/ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db876d4..a0265e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,9 +45,6 @@ jobs: - name: build run: cargo build - # skip main doc test because it opens a window - - run: sed -i 's/```rust/```no_run/g' src/lib.rs - - name: clippy run: cargo clippy --all-features -- -D warnings @@ -57,7 +54,9 @@ jobs: - name: headless tests uses: coactions/setup-xvfb@v1 with: - run: cargo test --all-features --verbose + # skip main doc test because it opens a window + # run: cargo test --all-features --verbose + run: cargo test --all-features --verbose --lib --tests docs: name: Documentation From 2428695c7f72676be4aec4bef1abb5febaf381b9 Mon Sep 17 00:00:00 2001 From: threefold3 Date: Mon, 2 Mar 2026 22:26:32 +0100 Subject: [PATCH 10/13] style: remove `main()` in crate doctest example --- src/lib.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ef18c16..cac052f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 From e093c0680746738b1b4cdf040acd72583499599f Mon Sep 17 00:00:00 2001 From: threefold3 Date: Tue, 3 Mar 2026 09:11:02 +0100 Subject: [PATCH 11/13] ci: skip headless tests to populate the cargo cache --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0265e7..fa011d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,12 +51,12 @@ jobs: - name: check formatting run: cargo fmt --all -- --check - - name: headless tests - 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 + # 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 docs: name: Documentation From 83c48b4c312549c95f84fe3c7bb22f3779dd9b19 Mon Sep 17 00:00:00 2001 From: threefold3 Date: Fri, 13 Mar 2026 22:05:50 +0100 Subject: [PATCH 12/13] tests: always save images to `tests/outputs` This will be used to have artifacts to inspect in case of CI failure. --- .gitignore | 1 + tests/3d_plot.rs | 2 +- tests/chart.rs | 2 +- tests/full_palette.rs | 7 +++++- tests/mandelbrot.rs | 2 +- tests/test_utils.rs | 58 +++++++++++++++++++++++++++++++------------ 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 3072d78..423bc6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .aider* target +/tests/outputs diff --git a/tests/3d_plot.rs b/tests/3d_plot.rs index 15c7547..1e7aa35 100644 --- a/tests/3d_plot.rs +++ b/tests/3d_plot.rs @@ -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(backend: WxBackend) -> anyhow::Result<()> { diff --git a/tests/chart.rs b/tests/chart.rs index d14e61c..d36eb45 100644 --- a/tests/chart.rs +++ b/tests/chart.rs @@ -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(backend: WxBackend) -> anyhow::Result<()> { diff --git a/tests/full_palette.rs b/tests/full_palette.rs index c1d04b7..4b8a4a9 100644 --- a/tests/full_palette.rs +++ b/tests/full_palette.rs @@ -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( backend: WxBackend, diff --git a/tests/mandelbrot.rs b/tests/mandelbrot.rs index 9314494..30febae 100644 --- a/tests/mandelbrot.rs +++ b/tests/mandelbrot.rs @@ -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(backend: WxBackend) -> Result<()> { diff --git a/tests/test_utils.rs b/tests/test_utils.rs index bcfa4b9..821620a 100644 --- a/tests/test_utils.rs +++ b/tests/test_utils.rs @@ -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}; @@ -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 @@ -33,14 +38,26 @@ use wxdragon::{self as wx}; pub fn run_plotters_image_test( width: u32, height: u32, - path_root: &str, + reference_png: impl Into, draw_fn: F, ) -> Result<()> where F: FnOnce(WxBackend) -> 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 @@ -69,27 +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 { - // regenerate expected image by uncommenting this and - // commenting the non-regression part - 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) } From 43df4aee82ae67363cc8686dc116b95951923e95 Mon Sep 17 00:00:00 2001 From: threefold3 Date: Fri, 13 Mar 2026 22:08:33 +0100 Subject: [PATCH 13/13] ci: run tests differently on linux & windows, save produced images --- .github/workflows/ci.yml | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa011d4..7ad3811 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,12 +51,28 @@ jobs: - name: check formatting run: cargo fmt --all -- --check - # - name: headless tests - # 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 == '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