diff --git a/Cargo.lock b/Cargo.lock index 69c14c5..b286614 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -303,6 +303,12 @@ dependencies = [ "zbus", ] +[[package]] +name = "assertables" +version = "9.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59051ec02907378a67b0ba1b8631121f5388c8dbbb3cec8c749d8f93c2c3c211" + [[package]] name = "async-broadcast" version = "0.7.2" @@ -1192,9 +1198,13 @@ name = "egui_plot" version = "0.34.0" dependencies = [ "ahash", + "assertables", "document-features", "egui", + "egui_kittest", "emath", + "env_logger", + "log", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index e7a3eba..b292047 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ ahash = { version = "0.8.12", default-features = false, features = [ "no-rng", # we don't need DOS-protection, so we let users opt-in to it instead "std", ] } +assertables = "9.8.2" document-features = "0.2.12" eframe = { version = "0.33", default-features = false } egui = { version = "0.33", default-features = false } @@ -279,3 +280,7 @@ zero_sized_map_values = "warn" manual_range_contains = "allow" # this is better on 'allow' map_unwrap_or = "allow" # this is better on 'allow' + + +[workspace.metadata.cargo-shear] +ignored = ["log", "assertables"] diff --git a/egui_plot/Cargo.toml b/egui_plot/Cargo.toml index 58e9e31..624f318 100644 --- a/egui_plot/Cargo.toml +++ b/egui_plot/Cargo.toml @@ -45,3 +45,9 @@ ahash.workspace = true document-features = { workspace = true, optional = true } serde = { workspace = true, optional = true } + +[dev-dependencies] +assertables.workspace = true +egui_kittest.workspace = true +env_logger.workspace = true +log.workspace = true diff --git a/egui_plot/src/plot.rs b/egui_plot/src/plot.rs index b6fd308..491913e 100644 --- a/egui_plot/src/plot.rs +++ b/egui_plot/src/plot.rs @@ -2125,3 +2125,44 @@ impl<'a> PlotUi<'a> { self.items.push(Box::new(heatmap)); } } + +#[cfg(all(test, not(target_arch = "wasm32")))] +mod plot_tests { + use super::*; + use crate::{Plot, Points}; + + use egui_kittest::Harness; + + /// Test that `auto_bounds(true)` fits the data exactly when the margin fraction is zero. + #[test] + fn auto_bounds_true() { + crate::utils::init_test_logger(); + + const MIN: [f64; 2] = [-10.0, -10.0]; + const MAX: [f64; 2] = [10.0, 10.0]; + + let harness = { + let bounds = PlotBounds::from_min_max(MIN, MAX); + Harness::new_ui_state( + |ui, bounds| { + let plot = Plot::new("test_plot") + .auto_bounds(true) + .set_margin_fraction(Vec2::splat(0.0)); + + plot.show(ui, |plot_ui| { + let corners = vec![MIN, MAX]; + plot_ui.points(Points::new("filler", corners)); + *bounds = plot_ui.plot_bounds(); + }); + }, + bounds, + ) + }; + + let final_bounds = harness.state(); + assert!((final_bounds.min[0] - MIN[0]).abs() <= f64::EPSILON); + assert!((final_bounds.min[1] - MIN[1]).abs() <= f64::EPSILON); + assert!((final_bounds.max[0] - MAX[0]).abs() <= f64::EPSILON); + assert!((final_bounds.max[1] - MAX[1]).abs() <= f64::EPSILON); + } +} diff --git a/egui_plot/src/utils.rs b/egui_plot/src/utils.rs index e7cbe41..739b286 100644 --- a/egui_plot/src/utils.rs +++ b/egui_plot/src/utils.rs @@ -56,3 +56,39 @@ pub(crate) fn find_name_candidate(name: &str, width: f32, painter: &Painter, fon best } + +/// Initialize logging so that the testing framework can capture log output. +/// +/// Call this at the top of a test function to see log output from that test. +/// The logging output will only be shown when the following conditions are met: +/// - The test fails +/// - The `RUST_LOG` environment variable is set to a level at or below the message level +/// +/// When running a specific test: +/// ```sh +/// RUST_LOG=info cargo test auto_bounds_true +/// ``` +/// +/// If the something causes the test to panic so hard that it never shows logging output, +/// you can use `--nocapture` to see log output as it happens: +/// ```sh +/// RUST_LOG=info cargo test auto_bounds_true -- --nocapture +/// ``` +#[cfg(test)] +pub(crate) fn init_test_logger() { + use std::io::Write as _; + let _result: Result<(), log::SetLoggerError> = env_logger::builder() + .is_test(true) + .format(|buf, record| { + let level_style = buf.default_level_style(record.level()); + writeln!( + buf, + "[{level_style}{}{level_style:#} {}:{}] {}", + record.level(), + record.file().unwrap_or("unknown"), + record.line().unwrap_or(0), + record.args() + ) + }) + .try_init(); +}