1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 06:10:06 -04:00

kittest: Add UPDATE_SNAPSHOTS=force (#7508)

This adds a new mode, `UPDATE_SNAPSHOTS=force`, which will lower the
threshold to zero, overwriting every image that is not _exactly_ the
same.

Most comparisons has a threshold because different GPUs render slightly
differently. However, setting that threshold accurately can be hard.

Sometimes a test will pass locally, but fail on CI. In those cases you
want to force an update of the failing test. You can use
`UPDATE_SNAPSHOTS=force` for that.

And sometimes a small change _should_ update all images, but the change
is so tiny that it falls under the threshold. Still, you want to make a
point of showing that these images have changes. You can use
`UPDATE_SNAPSHOTS=force` for that.
This commit is contained in:
Emil Ernerfeldt
2025-09-05 16:45:36 +02:00
committed by GitHub
parent eceb0b11c9
commit aedd43c88f
75 changed files with 213 additions and 171 deletions

View File

@@ -285,14 +285,34 @@ impl Display for SnapshotError {
}
}
/// If this is set, we update the snapshots (if different),
/// and _succeed_ the test.
/// This is so that you can set `UPDATE_SNAPSHOTS=true` and update _all_ tests,
/// without `cargo test` failing on the first failing crate.
fn should_update_snapshots() -> bool {
match std::env::var("UPDATE_SNAPSHOTS") {
Ok(value) => !matches!(value.as_str(), "false" | "0" | "no" | "off"),
Err(_) => false,
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Mode {
Test,
UpdateFailing,
UpdateAll,
}
impl Mode {
fn from_env() -> Self {
let Ok(value) = std::env::var("UPDATE_SNAPSHOTS") else {
return Self::Test;
};
match value.as_str() {
"false" | "0" | "no" | "off" => Self::Test,
"true" | "1" | "yes" | "on" => Self::UpdateFailing,
"force" => Self::UpdateAll,
unknown => {
panic!("Unsupported value for UPDATE_SNAPSHOTS: {unknown:?}");
}
}
}
fn is_update(&self) -> bool {
match self {
Self::Test => false,
Self::UpdateFailing | Self::UpdateAll => true,
}
}
}
@@ -330,6 +350,8 @@ fn try_image_snapshot_options_impl(
) -> SnapshotResult {
#![expect(clippy::print_stdout)]
let mode = Mode::from_env();
let SnapshotOptions {
threshold,
output_path,
@@ -386,7 +408,7 @@ fn try_image_snapshot_options_impl(
Ok(image) => image.to_rgba8(),
Err(err) => {
// No previous snapshot - probablye a new test.
if should_update_snapshots() {
if mode.is_update() {
return update_snapshot();
} else {
return Err(SnapshotError::OpenSnapshot {
@@ -398,7 +420,7 @@ fn try_image_snapshot_options_impl(
};
if previous.dimensions() != new.dimensions() {
if should_update_snapshots() {
if mode.is_update() {
return update_snapshot();
} else {
return Err(SnapshotError::SizeMismatch {
@@ -410,29 +432,45 @@ fn try_image_snapshot_options_impl(
}
// Compare existing image to the new one:
let result =
dify::diff::get_results(previous, new.clone(), *threshold, true, None, &None, &None);
let threshold = if mode == Mode::UpdateAll {
0.0
} else {
*threshold
};
if let Some((num_wrong_pixels, result_image)) = result {
result_image
let result =
dify::diff::get_results(previous, new.clone(), threshold, true, None, &None, &None);
if let Some((num_wrong_pixels, diff_image)) = result {
diff_image
.save(diff_path.clone())
.map_err(|err| SnapshotError::WriteSnapshot {
path: diff_path.clone(),
err,
})?;
if num_wrong_pixels as i64 <= *failed_pixel_count_threshold as i64 {
return Ok(());
}
let is_sameish = num_wrong_pixels as i64 <= *failed_pixel_count_threshold as i64;
if should_update_snapshots() {
update_snapshot()
} else {
Err(SnapshotError::Diff {
name,
diff: num_wrong_pixels,
diff_path,
})
match mode {
Mode::Test => {
if is_sameish {
Ok(())
} else {
Err(SnapshotError::Diff {
name,
diff: num_wrong_pixels,
diff_path,
})
}
}
Mode::UpdateFailing => {
if is_sameish {
Ok(())
} else {
update_snapshot()
}
}
Mode::UpdateAll => update_snapshot(),
}
} else {
Ok(())