-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Add kittest.toml config file
#7643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| use crate::OsThreshold; | ||
| use std::io; | ||
| use std::path::PathBuf; | ||
|
|
||
| /// Configuration for `egui_kittest`. | ||
| /// | ||
| /// It's loaded once (per process) by searching for a `kittest.toml` file in the project root | ||
| /// (the directory containing `Cargo.lock`). | ||
| #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] | ||
| #[serde(default, deny_unknown_fields)] | ||
| pub struct Config { | ||
| output_path: PathBuf, | ||
|
|
||
| threshold: f32, | ||
| failed_pixel_count_threshold: usize, | ||
|
|
||
| windows: OsConfig, | ||
| mac: OsConfig, | ||
| linux: OsConfig, | ||
| } | ||
|
|
||
| impl Default for Config { | ||
| fn default() -> Self { | ||
| Self { | ||
| output_path: PathBuf::from("tests/snapshots"), | ||
| threshold: 0.6, | ||
| failed_pixel_count_threshold: 0, | ||
| windows: Default::default(), | ||
| mac: Default::default(), | ||
| linux: Default::default(), | ||
| } | ||
| } | ||
| } | ||
| #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] | ||
| #[serde(default, deny_unknown_fields)] | ||
| pub struct OsConfig { | ||
| threshold: Option<f32>, | ||
| failed_pixel_count_threshold: Option<usize>, | ||
| } | ||
|
|
||
| fn find_project_root() -> io::Result<std::path::PathBuf> { | ||
| let mut current_dir = std::env::current_dir()?; | ||
|
|
||
| loop { | ||
| // Check if Cargo.toml exists in this directory | ||
| if current_dir.join("Cargo.lock").exists() { | ||
| return Ok(current_dir); | ||
| } | ||
|
|
||
| // Move up one directory | ||
| if !current_dir.pop() { | ||
| return Err(io::Error::new( | ||
| io::ErrorKind::NotFound, | ||
| "Project root not found", | ||
| )); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn load_config() -> Config { | ||
| let project_root = find_project_root(); | ||
|
|
||
| if let Ok(project_root) = project_root { | ||
| let config_path = project_root.join("kittest.toml"); | ||
| if config_path.exists() { | ||
| let config_str = | ||
| std::fs::read_to_string(config_path).expect("Failed to read config file"); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The error should mention the path |
||
| match toml::from_str(&config_str) { | ||
| Ok(config) => return config, | ||
| Err(e) => panic!("Failed to parse config file: {e}"), | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The error should mention the path |
||
| }; | ||
| } | ||
| } | ||
|
|
||
| Config::default() | ||
| } | ||
|
|
||
| pub fn config() -> &'static Config { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. docstring |
||
| Config::get() | ||
| } | ||
|
|
||
| impl Config { | ||
| pub fn get() -> &'static Self { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. docstring! Also, maybe call this |
||
| static INSTANCE: std::sync::LazyLock<Config> = std::sync::LazyLock::new(load_config); | ||
| &INSTANCE | ||
| } | ||
|
|
||
| pub fn os_threshold(&self) -> OsThreshold<f32> { | ||
| let fallback = self.threshold; | ||
| OsThreshold { | ||
| windows: self.windows.threshold.unwrap_or(fallback), | ||
| macos: self.mac.threshold.unwrap_or(fallback), | ||
| linux: self.linux.threshold.unwrap_or(fallback), | ||
| fallback, | ||
| } | ||
| } | ||
|
|
||
| pub fn os_failed_pixel_count_threshold(&self) -> OsThreshold<usize> { | ||
| let fallback = self.failed_pixel_count_threshold; | ||
| OsThreshold { | ||
| windows: self | ||
| .windows | ||
| .failed_pixel_count_threshold | ||
| .unwrap_or(fallback), | ||
| macos: self.mac.failed_pixel_count_threshold.unwrap_or(fallback), | ||
| linux: self.linux.failed_pixel_count_threshold.unwrap_or(fallback), | ||
| fallback, | ||
| } | ||
| } | ||
|
|
||
| /// The threshold. | ||
| /// | ||
| /// Default is 1.0. | ||
| pub fn threshold(&self) -> f32 { | ||
| self.os_threshold().threshold() | ||
| } | ||
|
|
||
| /// The number of pixels that can differ before the test is considered failed. | ||
| /// | ||
| /// Default is 0. | ||
| pub fn failed_pixel_count_threshold(&self) -> usize { | ||
| self.os_failed_pixel_count_threshold().threshold() | ||
| } | ||
|
|
||
| /// The output path for image snapshots. | ||
| /// | ||
| /// Default is "tests/snapshots". | ||
| pub fn output_path(&self) -> PathBuf { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To where is the path relative? |
||
| self.output_path.clone() | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,27 +1,32 @@ | ||
| use crate::Harness; | ||
| use image::ImageError; | ||
| use std::fmt::Display; | ||
| use std::io::ErrorKind; | ||
| use std::path::PathBuf; | ||
|
|
||
| use image::ImageError; | ||
|
|
||
| use crate::{Harness, config}; | ||
|
|
||
| pub type SnapshotResult = Result<(), SnapshotError>; | ||
|
|
||
| #[non_exhaustive] | ||
| #[derive(Clone, Debug)] | ||
| pub struct SnapshotOptions { | ||
| /// The threshold for the image comparison. | ||
| /// | ||
| /// The default is `0.6` (which is enough for most egui tests to pass across different | ||
| /// wgpu backends). | ||
| pub threshold: f32, | ||
|
|
||
| /// The number of pixels that can differ before the snapshot is considered a failure. | ||
| /// | ||
| /// Preferably, you should use `threshold` to control the sensitivity of the image comparison. | ||
| /// As a last resort, you can use this to allow a certain number of pixels to differ. | ||
| /// If `None`, the default is `0` (meaning no pixels can differ). | ||
| /// If `Some`, the value can be set per OS | ||
| pub failed_pixel_count_threshold: usize, | ||
|
|
||
| /// The path where the snapshots will be saved. | ||
| /// | ||
| /// The default is `tests/snapshots`. | ||
| pub output_path: PathBuf, | ||
| } | ||
|
|
@@ -30,7 +35,9 @@ pub struct SnapshotOptions { | |
| /// | ||
| /// This is useful if you want to set different thresholds for different operating systems. | ||
| /// | ||
| /// The default values are 0 / 0.0 | ||
| /// [`OsThreshold::default`] gets the default from the config file (`kittest.toml`). | ||
| /// For `usize`, it's the `failed_pixel_count_threshold` value. | ||
| /// For `f32`, it's the `threshold` value. | ||
| /// | ||
| /// Example usage: | ||
| /// ```no_run | ||
|
|
@@ -53,12 +60,36 @@ pub struct OsThreshold<T> { | |
| pub fallback: T, | ||
| } | ||
|
|
||
| impl Default for OsThreshold<usize> { | ||
| /// Returns the default `failed_pixel_count_threshold` as configured in `kittest.toml` | ||
| /// | ||
| /// The default is `0`. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't the default whatever is in |
||
| fn default() -> Self { | ||
| config().os_failed_pixel_count_threshold() | ||
| } | ||
| } | ||
|
|
||
| impl Default for OsThreshold<f32> { | ||
| /// Returns the default `threshold` as configured in `kittest.toml` | ||
| /// | ||
| /// The default is `0.6`. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same |
||
| fn default() -> Self { | ||
| config().os_threshold() | ||
| } | ||
| } | ||
|
|
||
| impl From<usize> for OsThreshold<usize> { | ||
| fn from(value: usize) -> Self { | ||
| Self::new(value) | ||
| } | ||
| } | ||
|
|
||
| impl From<f32> for OsThreshold<f32> { | ||
| fn from(value: f32) -> Self { | ||
| Self::new(value) | ||
| } | ||
| } | ||
|
|
||
| impl<T> OsThreshold<T> | ||
| where | ||
| T: Copy, | ||
|
|
@@ -123,9 +154,9 @@ impl From<OsThreshold<Self>> for f32 { | |
| impl Default for SnapshotOptions { | ||
| fn default() -> Self { | ||
| Self { | ||
| threshold: 0.6, | ||
| output_path: PathBuf::from("tests/snapshots"), | ||
| failed_pixel_count_threshold: 0, // Default is 0, meaning no pixels can differ | ||
| threshold: config().threshold(), | ||
| output_path: config().output_path(), | ||
| failed_pixel_count_threshold: config().failed_pixel_count_threshold(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,10 @@ | ||||||
| output_path = "tests/snapshots" | ||||||
|
|
||||||
| # Other oses get a higher threshold so they can still run tests locally without failures due to small rendering | ||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| # differences. | ||||||
| # To update snapshots, update them via ./scripts/update_snapshots_from_ci.sh or via kitdiff | ||||||
| threshold = 2.0 | ||||||
|
|
||||||
| [mac] | ||||||
| # Since our CI runs snapshot tests on macOS, this is our source of truth. | ||||||
| threshold = 0.6 | ||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tried to go lower here, but the redering test fails with a 0.6 between CI mac and my mac. The other tests are fine with 0.1 though |
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, we look for the first parent with a
kittest.tomlin it? 🤷