diff options
Diffstat (limited to 'vendor/snapbox/src/report')
-rw-r--r-- | vendor/snapbox/src/report/color.rs | 127 | ||||
-rw-r--r-- | vendor/snapbox/src/report/diff.rs | 384 | ||||
-rw-r--r-- | vendor/snapbox/src/report/mod.rs | 9 |
3 files changed, 520 insertions, 0 deletions
diff --git a/vendor/snapbox/src/report/color.rs b/vendor/snapbox/src/report/color.rs new file mode 100644 index 000000000..f1cd363b4 --- /dev/null +++ b/vendor/snapbox/src/report/color.rs @@ -0,0 +1,127 @@ +#[derive(Copy, Clone, Debug)] +#[allow(dead_code)] +pub struct Palette { + pub(crate) info: styled::Style, + pub(crate) warn: styled::Style, + pub(crate) error: styled::Style, + pub(crate) hint: styled::Style, + pub(crate) expected: styled::Style, + pub(crate) actual: styled::Style, +} + +impl Palette { + #[cfg(feature = "color")] + pub fn always() -> Self { + Self { + info: styled::Style(yansi::Style::new(yansi::Color::Green)), + warn: styled::Style(yansi::Style::new(yansi::Color::Yellow)), + error: styled::Style(yansi::Style::new(yansi::Color::Red)), + hint: styled::Style(yansi::Style::new(yansi::Color::Unset).dimmed()), + expected: styled::Style(yansi::Style::new(yansi::Color::Green).underline()), + actual: styled::Style(yansi::Style::new(yansi::Color::Red).underline()), + } + } + + #[cfg(not(feature = "color"))] + pub fn always() -> Self { + Self::never() + } + + pub fn never() -> Self { + Self { + info: Default::default(), + warn: Default::default(), + error: Default::default(), + hint: Default::default(), + expected: Default::default(), + actual: Default::default(), + } + } + + pub fn auto() -> Self { + if is_colored() { + Self::always() + } else { + Self::never() + } + } + + pub fn info<D: std::fmt::Display>(self, item: D) -> Styled<D> { + self.info.paint(item) + } + + pub fn warn<D: std::fmt::Display>(self, item: D) -> Styled<D> { + self.warn.paint(item) + } + + pub fn error<D: std::fmt::Display>(self, item: D) -> Styled<D> { + self.error.paint(item) + } + + pub fn hint<D: std::fmt::Display>(self, item: D) -> Styled<D> { + self.hint.paint(item) + } + + pub fn expected<D: std::fmt::Display>(self, item: D) -> Styled<D> { + self.expected.paint(item) + } + + pub fn actual<D: std::fmt::Display>(self, item: D) -> Styled<D> { + self.actual.paint(item) + } +} + +fn is_colored() -> bool { + #[cfg(feature = "color")] + { + concolor::get(concolor::Stream::Either).ansi_color() + } + + #[cfg(not(feature = "color"))] + { + false + } +} + +pub(crate) use styled::Style; +pub use styled::Styled; + +#[cfg(feature = "color")] +mod styled { + #[derive(Copy, Clone, Debug, Default)] + pub(crate) struct Style(pub(crate) yansi::Style); + + impl Style { + pub(crate) fn paint<T: std::fmt::Display>(self, item: T) -> Styled<T> { + Styled(self.0.paint(item)) + } + } + + pub struct Styled<D: std::fmt::Display>(yansi::Paint<D>); + + impl<D: std::fmt::Display> std::fmt::Display for Styled<D> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } +} + +#[cfg(not(feature = "color"))] +mod styled { + #[derive(Copy, Clone, Debug, Default)] + pub(crate) struct Style; + + impl Style { + pub(crate) fn paint<T: std::fmt::Display>(self, item: T) -> Styled<T> { + Styled(item) + } + } + + pub struct Styled<D: std::fmt::Display>(D); + + impl<D: std::fmt::Display> std::fmt::Display for Styled<D> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } +} diff --git a/vendor/snapbox/src/report/diff.rs b/vendor/snapbox/src/report/diff.rs new file mode 100644 index 000000000..adc9f7935 --- /dev/null +++ b/vendor/snapbox/src/report/diff.rs @@ -0,0 +1,384 @@ +pub fn write_diff( + writer: &mut dyn std::fmt::Write, + expected: &crate::Data, + actual: &crate::Data, + expected_name: Option<&dyn std::fmt::Display>, + actual_name: Option<&dyn std::fmt::Display>, + palette: crate::report::Palette, +) -> Result<(), std::fmt::Error> { + #[allow(unused_mut)] + let mut rendered = false; + #[cfg(feature = "diff")] + if let (Some(expected), Some(actual)) = (expected.render(), actual.render()) { + write_diff_inner( + writer, + &expected, + &actual, + expected_name, + actual_name, + palette, + )?; + rendered = true; + } + + if !rendered { + if let Some(expected_name) = expected_name { + writeln!(writer, "{} {}:", expected_name, palette.info("(expected)"))?; + } else { + writeln!(writer, "{}:", palette.info("Expected"))?; + } + writeln!(writer, "{}", palette.info(&expected))?; + if let Some(actual_name) = actual_name { + writeln!(writer, "{} {}:", actual_name, palette.error("(actual)"))?; + } else { + writeln!(writer, "{}:", palette.error("Actual"))?; + } + writeln!(writer, "{}", palette.error(&actual))?; + } + Ok(()) +} + +#[cfg(feature = "diff")] +fn write_diff_inner( + writer: &mut dyn std::fmt::Write, + expected: &str, + actual: &str, + expected_name: Option<&dyn std::fmt::Display>, + actual_name: Option<&dyn std::fmt::Display>, + palette: crate::report::Palette, +) -> Result<(), std::fmt::Error> { + let timeout = std::time::Duration::from_millis(500); + let min_elide = 20; + let context = 5; + + let changes = similar::TextDiff::configure() + .algorithm(similar::Algorithm::Patience) + .timeout(timeout) + .newline_terminated(false) + .diff_lines(expected, actual); + + writeln!(writer)?; + if let Some(expected_name) = expected_name { + writeln!( + writer, + "{}", + palette.info(format_args!("{:->4} expected: {}", "", expected_name)) + )?; + } else { + writeln!(writer, "{}", palette.info(format_args!("--- Expected")))?; + } + if let Some(actual_name) = actual_name { + writeln!( + writer, + "{}", + palette.error(format_args!("{:+>4} actual: {}", "", actual_name)) + )?; + } else { + writeln!(writer, "{}", palette.error(format_args!("+++ Actual")))?; + } + let changes = changes + .ops() + .iter() + .flat_map(|op| changes.iter_inline_changes(op)) + .collect::<Vec<_>>(); + let tombstones = if min_elide < changes.len() { + let mut tombstones = vec![true; changes.len()]; + + let mut counter = context; + for (i, change) in changes.iter().enumerate() { + match change.tag() { + similar::ChangeTag::Insert | similar::ChangeTag::Delete => { + counter = context; + tombstones[i] = false; + } + similar::ChangeTag::Equal => { + if counter != 0 { + tombstones[i] = false; + counter -= 1; + } + } + } + } + + let mut counter = context; + for (i, change) in changes.iter().enumerate().rev() { + match change.tag() { + similar::ChangeTag::Insert | similar::ChangeTag::Delete => { + counter = context; + tombstones[i] = false; + } + similar::ChangeTag::Equal => { + if counter != 0 { + tombstones[i] = false; + counter -= 1; + } + } + } + } + tombstones + } else { + Vec::new() + }; + + let mut elided = false; + for (i, change) in changes.into_iter().enumerate() { + if tombstones.get(i).copied().unwrap_or(false) { + if !elided { + let sign = "⋮"; + + write!(writer, "{:>4} ", " ",)?; + write!(writer, "{:>4} ", " ",)?; + writeln!(writer, "{}", palette.hint(sign))?; + } + elided = true; + } else { + elided = false; + match change.tag() { + similar::ChangeTag::Insert => { + write_change(writer, change, "+", palette.actual, palette.error, palette)?; + } + similar::ChangeTag::Delete => { + write_change(writer, change, "-", palette.expected, palette.info, palette)?; + } + similar::ChangeTag::Equal => { + write_change(writer, change, "|", palette.hint, palette.hint, palette)?; + } + } + } + } + + Ok(()) +} + +#[cfg(feature = "diff")] +fn write_change( + writer: &mut dyn std::fmt::Write, + change: similar::InlineChange<str>, + sign: &str, + em_style: crate::report::Style, + style: crate::report::Style, + palette: crate::report::Palette, +) -> Result<(), std::fmt::Error> { + if let Some(index) = change.old_index() { + write!(writer, "{:>4} ", palette.hint(index + 1),)?; + } else { + write!(writer, "{:>4} ", " ",)?; + } + if let Some(index) = change.new_index() { + write!(writer, "{:>4} ", palette.hint(index + 1),)?; + } else { + write!(writer, "{:>4} ", " ",)?; + } + write!(writer, "{} ", style.paint(sign))?; + for &(emphasized, change) in change.values() { + let cur_style = if emphasized { em_style } else { style }; + write!(writer, "{}", cur_style.paint(change))?; + } + if change.missing_newline() { + writeln!(writer, "{}", em_style.paint("∅"))?; + } + + Ok(()) +} + +#[cfg(test)] +mod test { + use super::*; + + #[cfg(feature = "diff")] + #[test] + fn diff_eq() { + let expected = "Hello\nWorld\n"; + let expected_name = "A"; + let actual = "Hello\nWorld\n"; + let actual_name = "B"; + let palette = crate::report::Palette::never(); + + let mut actual_diff = String::new(); + write_diff_inner( + &mut actual_diff, + expected, + actual, + Some(&expected_name), + Some(&actual_name), + palette, + ) + .unwrap(); + let expected_diff = " +---- expected: A +++++ actual: B + 1 1 | Hello + 2 2 | World +"; + + assert_eq!(expected_diff, actual_diff); + } + + #[cfg(feature = "diff")] + #[test] + fn diff_ne_line_missing() { + let expected = "Hello\nWorld\n"; + let expected_name = "A"; + let actual = "Hello\n"; + let actual_name = "B"; + let palette = crate::report::Palette::never(); + + let mut actual_diff = String::new(); + write_diff_inner( + &mut actual_diff, + expected, + actual, + Some(&expected_name), + Some(&actual_name), + palette, + ) + .unwrap(); + let expected_diff = " +---- expected: A +++++ actual: B + 1 1 | Hello + 2 - World +"; + + assert_eq!(expected_diff, actual_diff); + } + + #[cfg(feature = "diff")] + #[test] + fn diff_eq_trailing_extra_newline() { + let expected = "Hello\nWorld"; + let expected_name = "A"; + let actual = "Hello\nWorld\n"; + let actual_name = "B"; + let palette = crate::report::Palette::never(); + + let mut actual_diff = String::new(); + write_diff_inner( + &mut actual_diff, + expected, + actual, + Some(&expected_name), + Some(&actual_name), + palette, + ) + .unwrap(); + let expected_diff = " +---- expected: A +++++ actual: B + 1 1 | Hello + 2 - World∅ + 2 + World +"; + + assert_eq!(expected_diff, actual_diff); + } + + #[cfg(feature = "diff")] + #[test] + fn diff_eq_trailing_newline_missing() { + let expected = "Hello\nWorld\n"; + let expected_name = "A"; + let actual = "Hello\nWorld"; + let actual_name = "B"; + let palette = crate::report::Palette::never(); + + let mut actual_diff = String::new(); + write_diff_inner( + &mut actual_diff, + expected, + actual, + Some(&expected_name), + Some(&actual_name), + palette, + ) + .unwrap(); + let expected_diff = " +---- expected: A +++++ actual: B + 1 1 | Hello + 2 - World + 2 + World∅ +"; + + assert_eq!(expected_diff, actual_diff); + } + + #[cfg(feature = "diff")] + #[test] + fn diff_eq_elided() { + let mut expected = String::new(); + expected.push_str("Hello\n"); + for i in 0..20 { + expected.push_str(&i.to_string()); + expected.push('\n'); + } + expected.push_str("World\n"); + for i in 0..20 { + expected.push_str(&i.to_string()); + expected.push('\n'); + } + expected.push_str("!\n"); + let expected_name = "A"; + + let mut actual = String::new(); + actual.push_str("Goodbye\n"); + for i in 0..20 { + actual.push_str(&i.to_string()); + actual.push('\n'); + } + actual.push_str("Moon\n"); + for i in 0..20 { + actual.push_str(&i.to_string()); + actual.push('\n'); + } + actual.push_str("?\n"); + let actual_name = "B"; + + let palette = crate::report::Palette::never(); + + let mut actual_diff = String::new(); + write_diff_inner( + &mut actual_diff, + &expected, + &actual, + Some(&expected_name), + Some(&actual_name), + palette, + ) + .unwrap(); + let expected_diff = " +---- expected: A +++++ actual: B + 1 - Hello + 1 + Goodbye + 2 2 | 0 + 3 3 | 1 + 4 4 | 2 + 5 5 | 3 + 6 6 | 4 + ⋮ + 17 17 | 15 + 18 18 | 16 + 19 19 | 17 + 20 20 | 18 + 21 21 | 19 + 22 - World + 22 + Moon + 23 23 | 0 + 24 24 | 1 + 25 25 | 2 + 26 26 | 3 + 27 27 | 4 + ⋮ + 38 38 | 15 + 39 39 | 16 + 40 40 | 17 + 41 41 | 18 + 42 42 | 19 + 43 - ! + 43 + ? +"; + + assert_eq!(expected_diff, actual_diff); + } +} diff --git a/vendor/snapbox/src/report/mod.rs b/vendor/snapbox/src/report/mod.rs new file mode 100644 index 000000000..6c9a238b8 --- /dev/null +++ b/vendor/snapbox/src/report/mod.rs @@ -0,0 +1,9 @@ +//! Utilities to report test results to users + +mod color; +mod diff; + +pub use color::Palette; +pub(crate) use color::Style; +pub use color::Styled; +pub use diff::write_diff; |