From 64fda804f2b7be420e6e89a9e39dbc57c37bd727 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 22 Jul 2024 16:15:14 -0500 Subject: [PATCH 1/4] fix(data): Clarify IntoData::type affects current type --- crates/snapbox/src/data/mod.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/crates/snapbox/src/data/mod.rs b/crates/snapbox/src/data/mod.rs index 9522ce20..f7815b34 100644 --- a/crates/snapbox/src/data/mod.rs +++ b/crates/snapbox/src/data/mod.rs @@ -74,6 +74,7 @@ impl IntoJson for S { } /// Convert to [`Data`] with modifiers for `expected` data +#[allow(clippy::wrong_self_convention)] pub trait IntoData: Sized { /// Remove default [`filters`][crate::filter] from this `expected` result fn raw(self) -> Data { @@ -140,10 +141,16 @@ pub trait IntoData: Sized { /// # } /// ``` #[cfg(feature = "json")] - fn json(self) -> Data { + fn is_json(self) -> Data { self.is(DataFormat::Json) } + #[cfg(feature = "json")] + #[deprecated(since = "0.6.13", note = "Replaced with `IntoData::is_json`")] + fn json(self) -> Data { + self.is_json() + } + /// Initialize as json lines or [`Error`][DataFormat::Error] /// /// This is generally used for `expected` data @@ -161,18 +168,30 @@ pub trait IntoData: Sized { /// # } /// ``` #[cfg(feature = "json")] - fn json_lines(self) -> Data { + fn is_jsonlines(self) -> Data { self.is(DataFormat::JsonLines) } + #[cfg(feature = "json")] + #[deprecated(since = "0.6.13", note = "Replaced with `IntoData::is_jsonlines`")] + fn json_lines(self) -> Data { + self.is_jsonlines() + } + /// Initialize as Term SVG /// /// This is generally used for `expected` data #[cfg(feature = "term-svg")] - fn term_svg(self) -> Data { + fn is_termsvg(self) -> Data { self.is(DataFormat::TermSvg) } + #[cfg(feature = "term-svg")] + #[deprecated(since = "0.6.13", note = "Replaced with `IntoData::is_termsvg`")] + fn term_svg(self) -> Data { + self.is_termsvg() + } + /// Convert to [`Data`], applying defaults fn into_data(self) -> Data; } From ffd587f4b6eb8a0595ffea237d8f83b7d57f47c1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jul 2024 11:02:50 -0500 Subject: [PATCH 2/4] docs(data): Resolve deprecations --- crates/snapbox/src/data/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/snapbox/src/data/mod.rs b/crates/snapbox/src/data/mod.rs index f7815b34..ae7c7a22 100644 --- a/crates/snapbox/src/data/mod.rs +++ b/crates/snapbox/src/data/mod.rs @@ -136,7 +136,7 @@ pub trait IntoData: Sized { /// use snapbox::str; /// /// let expected = str![[r#"{"hello": "world"}"#]] - /// .json(); + /// .is_json(); /// assert_eq!(expected.format(), snapbox::data::DataFormat::Json); /// # } /// ``` @@ -163,7 +163,7 @@ pub trait IntoData: Sized { /// use snapbox::str; /// /// let expected = str![[r#"{"hello": "world"}"#]] - /// .json_lines(); + /// .is_jsonlines(); /// assert_eq!(expected.format(), snapbox::data::DataFormat::JsonLines); /// # } /// ``` From e5bc462cc6e9b3861c58120e705c59fc5d4c0063 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jul 2024 12:15:29 -0500 Subject: [PATCH 3/4] test(assert): Show use case for 'is' vs 'against' --- crates/snapbox/tests/testsuite/assert.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/snapbox/tests/testsuite/assert.rs b/crates/snapbox/tests/testsuite/assert.rs index 6c34ad3c..bbf96ae9 100644 --- a/crates/snapbox/tests/testsuite/assert.rs +++ b/crates/snapbox/tests/testsuite/assert.rs @@ -1,4 +1,5 @@ use snapbox::assert_data_eq; +use snapbox::data::IntoData; use snapbox::file; use snapbox::str; @@ -26,3 +27,22 @@ line1 fn test_expect_file() { assert_data_eq!(include_str!("../../README.md"), file!["../../README.md"]); } + +#[test] +#[cfg(feature = "json")] +fn actual_expected_formats_differ() { + assert_data_eq!( + r#"{} +{"order": 1} +{"order": 2} +{"order": 3} +"#, + str![[r#" +{} +{"order":1} +{"order":2} +{"order":3} + +"#]].is_jsonlines(), + ); +} From 273025e49d7809eabbf6ec438257c60f4d80c96b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jul 2024 12:26:35 -0500 Subject: [PATCH 4/4] feat(assert): Allow comparing json to jsonlines Fixes #348 --- crates/snapbox/src/assert/mod.rs | 4 +- crates/snapbox/src/data/filters.rs | 17 ++++- crates/snapbox/src/data/mod.rs | 94 ++++++++++++++++++++++++ crates/snapbox/tests/testsuite/assert.rs | 19 +++-- 4 files changed, 125 insertions(+), 9 deletions(-) diff --git a/crates/snapbox/src/assert/mod.rs b/crates/snapbox/src/assert/mod.rs index 889c71bb..3f647a35 100644 --- a/crates/snapbox/src/assert/mod.rs +++ b/crates/snapbox/src/assert/mod.rs @@ -118,8 +118,8 @@ impl Assert { } // On `expected` being an error, make a best guess - let format = expected.intended_format(); - actual = actual.coerce_to(format); + actual = actual.coerce_to(expected.against_format()); + actual = actual.coerce_to(expected.intended_format()); if self.normalize_paths && expected.filters.is_paths_set() { actual = FilterPaths.filter(actual); diff --git a/crates/snapbox/src/data/filters.rs b/crates/snapbox/src/data/filters.rs index 5b0103b3..d9c95587 100644 --- a/crates/snapbox/src/data/filters.rs +++ b/crates/snapbox/src/data/filters.rs @@ -1,6 +1,9 @@ +use crate::data::DataFormat; + #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub(crate) struct FilterSet { flags: usize, + against: Option, } impl FilterSet { @@ -9,7 +12,10 @@ impl FilterSet { } pub(crate) const fn empty() -> Self { - Self { flags: 0 } + Self { + flags: 0, + against: None, + } } pub(crate) fn redactions(mut self) -> Self { @@ -32,6 +38,11 @@ impl FilterSet { self } + pub(crate) fn against(mut self, format: DataFormat) -> Self { + self.against = Some(format); + self + } + pub(crate) const fn is_redaction_set(&self) -> bool { self.is_set(Self::REDACTIONS) } @@ -47,6 +58,10 @@ impl FilterSet { pub(crate) const fn is_unordered_set(&self) -> bool { self.is_set(Self::UNORDERED) } + + pub(crate) const fn get_against(&self) -> Option { + self.against + } } impl FilterSet { diff --git a/crates/snapbox/src/data/mod.rs b/crates/snapbox/src/data/mod.rs index ae7c7a22..4704466a 100644 --- a/crates/snapbox/src/data/mod.rs +++ b/crates/snapbox/src/data/mod.rs @@ -192,6 +192,67 @@ pub trait IntoData: Sized { self.is_termsvg() } + /// Override the type this snapshot will be compared against + /// + /// Normally, the `actual` data is coerced to [`IntoData::is`]. + /// This allows overriding that so you can store your snapshot in a more readable, diffable + /// format. + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(feature = "json")] { + /// use snapbox::prelude::*; + /// use snapbox::str; + /// + /// let expected = str![[r#"{"hello": "world"}"#]] + /// .against(snapbox::data::DataFormat::JsonLines); + /// # } + /// ``` + fn against(self, format: DataFormat) -> Data { + self.into_data().against(format) + } + + /// Initialize as json or [`Error`][DataFormat::Error] + /// + /// This is generally used for `expected` data + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(feature = "json")] { + /// use snapbox::prelude::*; + /// use snapbox::str; + /// + /// let expected = str![[r#"{"hello": "world"}"#]] + /// .is_json(); + /// # } + /// ``` + #[cfg(feature = "json")] + fn against_json(self) -> Data { + self.against(DataFormat::Json) + } + + /// Initialize as json lines or [`Error`][DataFormat::Error] + /// + /// This is generally used for `expected` data + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(feature = "json")] { + /// use snapbox::prelude::*; + /// use snapbox::str; + /// + /// let expected = str![[r#"{"hello": "world"}"#]] + /// .against_jsonlines(); + /// # } + /// ``` + #[cfg(feature = "json")] + fn against_jsonlines(self) -> Data { + self.against(DataFormat::JsonLines) + } + /// Convert to [`Data`], applying defaults fn into_data(self) -> Data; } @@ -577,6 +638,29 @@ impl Data { }) } + /// Override the type this snapshot will be compared against + /// + /// Normally, the `actual` data is coerced to [`Data::is`]. + /// This allows overriding that so you can store your snapshot in a more readable, diffable + /// format. + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(feature = "json")] { + /// use snapbox::prelude::*; + /// use snapbox::str; + /// + /// let expected = str![[r#"{"hello": "world"}"#]] + /// .is(snapbox::data::DataFormat::Json) + /// .against(snapbox::data::DataFormat::JsonLines); + /// # } + /// ``` + fn against(mut self, format: DataFormat) -> Data { + self.filters = self.filters.against(format); + self + } + /// Convert `Self` to [`format`][DataFormat] if possible /// /// This is generally used on `actual` data to make it match `expected` @@ -592,6 +676,10 @@ impl Data { (DataInner::Json(inner), DataFormat::Json) => DataInner::Json(inner), #[cfg(feature = "json")] (DataInner::JsonLines(inner), DataFormat::JsonLines) => DataInner::JsonLines(inner), + #[cfg(feature = "json")] + (DataInner::JsonLines(inner), DataFormat::Json) => DataInner::Json(inner), + #[cfg(feature = "json")] + (DataInner::Json(inner), DataFormat::JsonLines) => DataInner::JsonLines(inner), #[cfg(feature = "term-svg")] (DataInner::TermSvg(inner), DataFormat::TermSvg) => DataInner::TermSvg(inner), (DataInner::Binary(inner), _) => { @@ -705,6 +793,12 @@ impl Data { } } + pub(crate) fn against_format(&self) -> DataFormat { + self.filters + .get_against() + .unwrap_or_else(|| self.intended_format()) + } + pub(crate) fn relevant(&self) -> Option<&str> { match &self.inner { DataInner::Error(_) => None, diff --git a/crates/snapbox/tests/testsuite/assert.rs b/crates/snapbox/tests/testsuite/assert.rs index bbf96ae9..d81a974b 100644 --- a/crates/snapbox/tests/testsuite/assert.rs +++ b/crates/snapbox/tests/testsuite/assert.rs @@ -38,11 +38,18 @@ fn actual_expected_formats_differ() { {"order": 3} "#, str![[r#" -{} -{"order":1} -{"order":2} -{"order":3} - -"#]].is_jsonlines(), +[ + {}, + { + "order": 1 + }, + { + "order": 2 + }, + { + "order": 3 + } +] +"#]].is_json().against_jsonlines(), ); }