egui_kittest: write `.old.png` files when updating images (#5578)

After running `UPDATE_SNAPSHOTS=1 cargo test --all-features` I want to
compare before/after images before committing them. Now I can!
This commit is contained in:
Emil Ernerfeldt 2025-01-03 16:23:31 +01:00 committed by GitHub
parent 5cbf337f18
commit 938d8b0d2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 42 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
**/target_wasm **/target_wasm
**/tests/snapshots/**/*.diff.png **/tests/snapshots/**/*.diff.png
**/tests/snapshots/**/*.new.png **/tests/snapshots/**/*.new.png
**/tests/snapshots/**/*.old.png
/.*.json /.*.json
/.vscode /.vscode
/media/* /media/*

View File

@ -2,7 +2,7 @@ use crate::Harness;
use image::ImageError; use image::ImageError;
use std::fmt::Display; use std::fmt::Display;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::path::{Path, PathBuf}; use std::path::PathBuf;
#[non_exhaustive] #[non_exhaustive]
pub struct SnapshotOptions { pub struct SnapshotOptions {
@ -159,22 +159,6 @@ fn should_update_snapshots() -> bool {
std::env::var("UPDATE_SNAPSHOTS").is_ok() std::env::var("UPDATE_SNAPSHOTS").is_ok()
} }
fn maybe_update_snapshot(
snapshot_path: &Path,
current: &image::RgbaImage,
) -> Result<(), SnapshotError> {
if should_update_snapshots() {
current
.save(snapshot_path)
.map_err(|err| SnapshotError::WriteSnapshot {
err,
path: snapshot_path.into(),
})?;
println!("Updated snapshot: {snapshot_path:?}");
}
Ok(())
}
/// Image snapshot test with custom options. /// Image snapshot test with custom options.
/// ///
/// If you want to change the default options for your whole project, it's recommended to create a /// If you want to change the default options for your whole project, it's recommended to create a
@ -188,11 +172,14 @@ fn maybe_update_snapshot(
/// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`. /// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`.
/// If new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. /// If new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`.
/// ///
/// If the env-var `UPDATE_SNAPSHOTS` is set, then the old image will backed up under `{output_path}/{name}.old.png`.
/// and then new image will be written to `{output_path}/{name}.png`
///
/// # Errors /// # Errors
/// Returns a [`SnapshotError`] if the image does not match the snapshot or if there was an error /// Returns a [`SnapshotError`] if the image does not match the snapshot or if there was an error
/// reading or writing the snapshot. /// reading or writing the snapshot.
pub fn try_image_snapshot_options( pub fn try_image_snapshot_options(
current: &image::RgbaImage, new: &image::RgbaImage,
name: &str, name: &str,
options: &SnapshotOptions, options: &SnapshotOptions,
) -> Result<(), SnapshotError> { ) -> Result<(), SnapshotError> {
@ -201,45 +188,72 @@ pub fn try_image_snapshot_options(
output_path, output_path,
} = options; } = options;
let path = output_path.join(format!("{name}.png")); std::fs::create_dir_all(output_path).ok();
std::fs::create_dir_all(path.parent().expect("Could not get snapshot folder")).ok();
// The one that is checked in to git
let snapshot_path = output_path.join(format!("{name}.png"));
// These should be in .gitignore:
let diff_path = output_path.join(format!("{name}.diff.png")); let diff_path = output_path.join(format!("{name}.diff.png"));
let current_path = output_path.join(format!("{name}.new.png")); let old_backup_path = output_path.join(format!("{name}.old.png"));
let new_path = output_path.join(format!("{name}.new.png"));
current // Delete old temporary files if they exist:
.save(&current_path) std::fs::remove_file(&diff_path).ok();
std::fs::remove_file(&old_backup_path).ok();
std::fs::remove_file(&new_path).ok();
let maybe_update_snapshot = || {
if should_update_snapshots() {
// Keep the old version so the user can compare it:
std::fs::rename(&snapshot_path, &old_backup_path).ok();
// Write the new file to the checked in path:
new.save(&snapshot_path)
.map_err(|err| SnapshotError::WriteSnapshot {
err,
path: snapshot_path.clone(),
})?;
// No need for an explicit `.new` file:
std::fs::remove_file(&new_path).ok();
println!("Updated snapshot: {snapshot_path:?}");
}
Ok(())
};
// Always write a `.new` file so the user can compare:
new.save(&new_path)
.map_err(|err| SnapshotError::WriteSnapshot { .map_err(|err| SnapshotError::WriteSnapshot {
err, err,
path: current_path, path: new_path.clone(),
})?; })?;
let previous = match image::open(&path) { let previous = match image::open(&snapshot_path) {
Ok(image) => image.to_rgba8(), Ok(image) => image.to_rgba8(),
Err(err) => { Err(err) => {
maybe_update_snapshot(&path, current)?; // No previous snapshot - probablye a new test.
return Err(SnapshotError::OpenSnapshot { path, err }); maybe_update_snapshot()?;
return Err(SnapshotError::OpenSnapshot {
path: snapshot_path.clone(),
err,
});
} }
}; };
if previous.dimensions() != current.dimensions() { if previous.dimensions() != new.dimensions() {
maybe_update_snapshot(&path, current)?; maybe_update_snapshot()?;
return Err(SnapshotError::SizeMismatch { return Err(SnapshotError::SizeMismatch {
name: name.to_owned(), name: name.to_owned(),
expected: previous.dimensions(), expected: previous.dimensions(),
actual: current.dimensions(), actual: new.dimensions(),
}); });
} }
let result = dify::diff::get_results( // Compare existing image to the new one:
previous, let result =
current.clone(), dify::diff::get_results(previous, new.clone(), *threshold, true, None, &None, &None);
*threshold,
true,
None,
&None,
&None,
);
if let Some((diff, result_image)) = result { if let Some((diff, result_image)) = result {
result_image result_image
@ -248,15 +262,13 @@ pub fn try_image_snapshot_options(
path: diff_path.clone(), path: diff_path.clone(),
err, err,
})?; })?;
maybe_update_snapshot(&path, current)?; maybe_update_snapshot()?;
Err(SnapshotError::Diff { Err(SnapshotError::Diff {
name: name.to_owned(), name: name.to_owned(),
diff, diff,
diff_path, diff_path,
}) })
} else { } else {
// Delete old diff if it exists
std::fs::remove_file(diff_path).ok();
Ok(()) Ok(())
} }
} }