build(rust): vendor googletest crate

This commit was merged in pull request #76.
This commit is contained in:
alexion committed 2026-09-19 14:53:22 -04:00
1 parent a6fe84190e
commit 6887b104d6
576 files changed
+284290 -2

No files matched your search

@@ -0,0 +1,148 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module; the declarative
// macro is documented in the matcher module.
#![doc(hidden)]
/// Matches a value which all of the given matchers match.
///
/// Each argument is a [`Matcher`][crate::matcher::Matcher] which matches
/// against the actual value.
///
/// For example:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!("A string", all!(starts_with("A"), ends_with("string")))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!("A string", all!(starts_with("A"), ends_with("not a string")))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
/// Using this macro is equivalent to using the
/// [`and`][crate::matcher::MatcherBase::and] method:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(10, gt(9).and(lt(11)))?; // Also passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// Assertion failure messages are not guaranteed to be identical, however.
///
/// If an inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
///
/// verify_that!(123, all![123, lt(1000), gt(100)])
/// # .unwrap();
/// ```
#[macro_export]
#[doc(hidden)]
macro_rules! __all {
($(,)?) => {{
$crate::matchers::anything()
}} ;
($matcher:expr $(,)?) => {{
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
auto_eq!($matcher)
}};
($head:expr, $head2:expr $(,)?) => {{
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
$crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher::new(auto_eq!($head), auto_eq!($head2))
}};
($head:expr, $head2:expr, $($tail:expr),+ $(,)?) => {{
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
$crate::__all![
$crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher::new(auto_eq!($head), auto_eq!($head2)),
$($tail),+
]
}}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn description_shows_more_than_one_matcher() -> Result<()> {
let first_matcher = starts_with("A");
let second_matcher = ends_with("string");
let matcher = all!(first_matcher, second_matcher);
verify_that!(
Matcher::<&String>::describe(&matcher, MatcherResult::Match),
displays_as(eq(indoc!(
"
has all the following properties:
* starts with prefix \"A\"
* ends with suffix \"string\""
)))
)
}
#[test]
fn description_shows_one_matcher_directly() -> Result<()> {
let first_matcher = starts_with("A");
let matcher = all!(first_matcher);
verify_that!(
Matcher::<&String>::describe(&matcher, MatcherResult::Match),
displays_as(eq("starts with prefix \"A\""))
)
}
#[test]
fn mismatch_description_shows_which_matcher_failed_if_more_than_one_constituent() -> Result<()>
{
let first_matcher = starts_with("Another");
let second_matcher = ends_with("string");
let matcher = all!(first_matcher, second_matcher);
verify_that!(
matcher.explain_match("A string"),
displays_as(eq("which does not start with \"Another\""))
)
}
#[test]
fn mismatch_description_is_simple_when_only_one_consistuent() -> Result<()> {
let first_matcher = starts_with("Another");
let matcher = all!(first_matcher);
verify_that!(
matcher.explain_match("A string"),
displays_as(eq("which does not start with \"Another\""))
)
}
#[test]
fn all_with_auto_eq() -> Result<()> {
verify_that!(42, all![eq(42), 42, lt(100)])
}
}
@@ -0,0 +1,155 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module; the declarative
// macro is documented in the matchers module.
#![doc(hidden)]
/// Matches a value which at least one of the given matchers match.
///
/// Each argument is a [`Matcher`][crate::matcher::Matcher] which matches
/// against the actual value.
///
/// For example:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!("A string", any!(starts_with("A"), ends_with("string")))?; // Passes
/// verify_that!("A string", any!(starts_with("A"), starts_with("string")))?; // Passes
/// verify_that!("A string", any!(ends_with("A"), ends_with("string")))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!("A string", any!(starts_with("An"), ends_with("not a string")))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
/// Using this macro is equivalent to using the
/// [`or`][crate::matcher::MatcherBase::or] method:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(10, gt(9).or(lt(8)))?; // Also passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// Assertion failure messages are not guaranteed to be identical, however.
///
/// If an inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
///
/// verify_that!(123, any![lt(1), 123, gt(1000)])
/// # .unwrap();
/// ```
#[macro_export]
#[doc(hidden)]
macro_rules! __any {
($(,)?) => {{
$crate::matchers::not($crate::matchers::anything())
}} ;
($matcher:expr $(,)?) => {{
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
auto_eq!($matcher)
}};
($head:expr, $head2:expr $(,)?) => {{
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
$crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher::new(auto_eq!($head), auto_eq!($head2))
}};
($head:expr, $head2:expr, $($tail:expr),+ $(,)?) => {{
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
$crate::__any![
$crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher::new(auto_eq!($head), auto_eq!($head2)),
$($tail),+
]
}}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn description_shows_more_than_one_matcher() -> Result<()> {
let first_matcher = starts_with("A");
let second_matcher = ends_with("string");
let matcher = any!(first_matcher, second_matcher);
verify_that!(
Matcher::<&String>::describe(&matcher, MatcherResult::Match),
displays_as(eq(indoc!(
"
has at least one of the following properties:
* starts with prefix \"A\"
* ends with suffix \"string\""
)))
)
}
#[test]
fn description_shows_one_matcher_directly() -> Result<()> {
let first_matcher = starts_with("A");
let matcher = any!(first_matcher);
verify_that!(
Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("starts with prefix \"A\""))
)
}
#[test]
fn mismatch_description_shows_which_matcher_failed_if_more_than_one_constituent() -> Result<()>
{
let first_matcher = starts_with("Another");
let second_matcher = ends_with("string");
let matcher = any!(first_matcher, second_matcher);
verify_that!(
matcher.explain_match("A string"),
displays_as(eq("which does not start with \"Another\""))
)
}
#[test]
fn mismatch_description_is_simple_when_only_one_constituent() -> Result<()> {
let first_matcher = starts_with("Another");
let matcher = any!(first_matcher);
verify_that!(
matcher.explain_match("A string"),
displays_as(eq("which does not start with \"Another\""))
)
}
#[test]
fn empty_any_matcher_never_matches() -> Result<()> {
verify_that!(123, not(any![]))
}
#[test]
fn any_with_auto_eq() -> Result<()> {
verify_that!(42, any![1, 2, 42, gt(123)])
}
}
@@ -0,0 +1,80 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches anything. This matcher always succeeds.
///
/// This is useful to check if `actual` matches the specific structure (like
/// `Some(...)`) but without caring about the internal value.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let option = Some("Some value");
/// verify_that!(option, some(anything()))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
pub fn anything() -> Anything {
Anything
}
#[derive(MatcherBase)]
pub struct Anything;
impl<T: Debug + Copy> Matcher<T> for Anything {
fn matches(&self, _: T) -> MatcherResult {
MatcherResult::Match
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => "is anything".into(),
MatcherResult::NoMatch => "never matches".into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
#[test]
fn anything_matches_i32() -> Result<()> {
let value = 32;
verify_that!(value, anything())?;
Ok(())
}
#[test]
fn anything_matches_str() -> Result<()> {
let value = "32";
verify_that!(value, anything())?;
Ok(())
}
#[test]
fn anything_matches_option() -> Result<()> {
let value = Some(32);
verify_that!(value, some(anything()))?;
Ok(())
}
}
@@ -0,0 +1,100 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
/// Matches boolean value `true`.
pub fn is_true() -> BoolMatcher {
BoolMatcher { expected: true }
}
/// Matches boolean value `false`.
pub fn is_false() -> BoolMatcher {
BoolMatcher { expected: false }
}
/// Matches a bool value or bool reference.
#[derive(MatcherBase)]
pub struct BoolMatcher {
expected: bool,
}
impl BoolMatcher {
fn matches(&self, actual: bool) -> MatcherResult {
(actual == self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match (matcher_result, self.expected) {
(MatcherResult::Match, true) | (MatcherResult::NoMatch, false) => "is true".into(),
(MatcherResult::Match, false) | (MatcherResult::NoMatch, true) => "is false".into(),
}
}
}
impl Matcher<bool> for BoolMatcher {
fn matches(&self, actual: bool) -> MatcherResult {
self.matches(actual)
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
self.describe(matcher_result)
}
}
impl<'a> Matcher<&'a bool> for BoolMatcher {
fn matches(&self, actual: &'a bool) -> MatcherResult {
self.matches(*actual)
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
self.describe(matcher_result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
use crate::Result;
#[test]
fn match_value() -> Result<()> {
verify_that!(true, is_true())?;
verify_that!(true, not(is_false()))?;
verify_that!(false, is_false())?;
verify_that!(false, not(is_true()))
}
#[test]
fn match_ref() -> Result<()> {
let t = true;
let f = false;
verify_that!(&t, is_true())?;
verify_that!(&t, not(is_false()))?;
verify_that!(&f, is_false())?;
verify_that!(&f, not(is_true()))
}
#[test]
fn describe() {
assert_eq!(is_true().describe(MatcherResult::Match).to_string(), "is true");
assert_eq!(is_true().describe(MatcherResult::NoMatch).to_string(), "is false");
assert_eq!(is_false().describe(MatcherResult::Match).to_string(), "is false");
assert_eq!(is_false().describe(MatcherResult::NoMatch).to_string(), "is true");
}
}
@@ -0,0 +1,164 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a string whose number of Unicode scalars matches `expected`.
///
/// In other words, the argument must match the output of
/// [`actual_string.chars().count()`][std::str::Chars].
///
/// This can have surprising effects when what appears to be a single character
/// is composed of multiple Unicode scalars. See [Rust documentation on
/// character
/// representation](https://doc.rust-lang.org/std/primitive.char.html#representation)
/// for more information.
///
/// This matches against owned strings and string slices.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let string_slice = "A string";
/// verify_that!(string_slice, char_count(eq(8)))?;
/// let non_ascii_string_slice = "Ä ſtřiɲğ";
/// verify_that!(non_ascii_string_slice, char_count(eq(8)))?;
/// let owned_string = String::from("A string");
/// verify_that!(owned_string, char_count(eq(8)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// The parameter `expected` can be any integer numeric matcher.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let string_slice = "A string";
/// verify_that!(string_slice, char_count(gt(4)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
pub fn char_count<E: Matcher<usize>>(expected: E) -> CharLenMatcher<E> {
CharLenMatcher { expected }
}
#[derive(MatcherBase)]
pub struct CharLenMatcher<E> {
expected: E,
}
impl<T: Debug + Copy + AsRef<str>, E: Matcher<usize>> Matcher<T> for CharLenMatcher<E> {
fn matches(&self, actual: T) -> MatcherResult {
self.expected.matches(actual.as_ref().chars().count())
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!(
"has character count, which {}",
self.expected.describe(MatcherResult::Match)
)
.into(),
MatcherResult::NoMatch => format!(
"has character count, which {}",
self.expected.describe(MatcherResult::NoMatch)
)
.into(),
}
}
fn explain_match(&self, actual: T) -> Description {
let actual_size = actual.as_ref().chars().count();
format!(
"which has character count {}, {}",
actual_size,
self.expected.explain_match(actual_size)
)
.into()
}
}
#[cfg(test)]
mod tests {
use crate::description::Description;
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::fmt::Debug;
#[test]
fn char_count_matches_string_slice() -> Result<()> {
let value = "abcd";
verify_that!(value, char_count(eq(4)))
}
#[test]
fn char_count_matches_owned_string() -> Result<()> {
let value = String::from("abcd");
verify_that!(value, char_count(eq(4)))
}
#[test]
fn char_count_counts_non_ascii_characters_correctly() -> Result<()> {
let value = "äöüß";
verify_that!(value, char_count(eq(4)))
}
#[test]
fn char_count_explains_match() -> Result<()> {
#[derive(MatcherBase)]
struct TestMatcher;
impl<T: Debug + Copy> Matcher<T> for TestMatcher {
fn matches(&self, _: T) -> MatcherResult {
false.into()
}
fn describe(&self, _: MatcherResult) -> Description {
"called described".into()
}
fn explain_match(&self, _: T) -> Description {
"called explain_match".into()
}
}
verify_that!(
char_count(TestMatcher).explain_match("A string"),
displays_as(eq("which has character count 8, called explain_match"))
)
}
#[test]
fn char_count_has_correct_failure_message() -> Result<()> {
let result = verify_that!("äöüß", char_count(eq(3)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
r#"
Value of: "äöüß"
Expected: has character count, which is equal to 3
Actual: "äöüß",
which has character count 4, which isn't equal to 3"#
))))
)
}
}
@@ -0,0 +1,218 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module.
#![doc(hidden)]
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matcher created by [`Matcher::and`] and [`all!`].
///
/// Both [`Matcher::and`] and [`all!`] nest on m1. In other words,
/// both `x.and(y).and(z)` and `all![x, y, z]` produce:
/// ```ignore
/// ConjunctionMatcher {
/// m1: ConjunctionMatcher {
/// m1: x,
/// m2: y
/// },
/// m2: z
/// }
/// ```
///
/// This behavior must be respected
/// to ensure that [`Matcher::explain_match`] and [`Matcher::describe`] produce
/// useful descriptions.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
#[derive(MatcherBase)]
pub struct ConjunctionMatcher<M1, M2> {
m1: M1,
m2: M2,
}
impl<M1, M2> ConjunctionMatcher<M1, M2> {
pub fn new(m1: M1, m2: M2) -> Self {
Self { m1, m2 }
}
}
impl<T: Debug + Copy, M1: Matcher<T>, M2: Matcher<T>> Matcher<T> for ConjunctionMatcher<M1, M2> {
fn matches(&self, actual: T) -> MatcherResult {
match (self.m1.matches(actual), self.m2.matches(actual)) {
(MatcherResult::Match, MatcherResult::Match) => MatcherResult::Match,
_ => MatcherResult::NoMatch,
}
}
fn explain_match(&self, actual: T) -> Description {
match (self.m1.matches(actual), self.m2.matches(actual)) {
(MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual),
(MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual),
(_, _) => {
let m1_description = self.m1.explain_match(actual);
if m1_description.is_conjunction_description() {
m1_description.nested(self.m2.explain_match(actual))
} else {
Description::new()
.bullet_list()
.collect([m1_description, self.m2.explain_match(actual)])
.conjunction_description()
}
}
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
let m1_description = self.m1.describe(matcher_result);
if m1_description.is_conjunction_description() {
m1_description.push_in_last_nested(self.m2.describe(matcher_result))
} else {
let header = if matcher_result.into() {
"has all the following properties:"
} else {
"has at least one of the following properties:"
};
Description::new()
.text(header)
.nested(
Description::new()
.bullet_list()
.collect([m1_description, self.m2.describe(matcher_result)]),
)
.conjunction_description()
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn and_true_true_matches() -> Result<()> {
verify_that!(1, anything().and(anything()))
}
#[test]
fn and_true_false_does_not_match() -> Result<()> {
let result = verify_that!(1, anything().and(not(anything())));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 1
Expected: has all the following properties:
* is anything
* never matches
Actual: 1,
which is anything
"
))))
)
}
#[test]
fn and_false_true_does_not_match() -> Result<()> {
let result = verify_that!(1, not(anything()).and(anything()));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 1
Expected: has all the following properties:
* never matches
* is anything
Actual: 1,
which is anything
"
))))
)
}
#[test]
fn and_false_false_does_not_match() -> Result<()> {
let result = verify_that!(1, not(anything()).and(not(anything())));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 1
Expected: has all the following properties:
* never matches
* never matches
Actual: 1,
* which is anything
* which is anything
"
))))
)
}
#[test]
fn and_long_chain_of_matchers() -> Result<()> {
let result = verify_that!(
1,
anything().and(not(anything())).and(anything()).and(not(anything())).and(anything())
);
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 1
Expected: has all the following properties:
* is anything
* never matches
* is anything
* never matches
* is anything
Actual: 1,
* which is anything
* which is anything
"
))))
)
}
#[test]
fn chained_and_matches() -> Result<()> {
#[derive(Debug, Clone, Copy)]
struct Struct {
a: i32,
b: i32,
c: i32,
}
verify_that!(
Struct { a: 1, b: 2, c: 3 },
field!(Struct.a, eq(1)).and(field!(Struct.b, eq(2))).and(field!(Struct.c, eq(3)))
)
}
#[test]
fn works_with_str_slices() -> Result<()> {
verify_that!("A string", starts_with("A").and(ends_with("string")))
}
#[test]
fn works_with_owned_strings() -> Result<()> {
verify_that!("A string".to_string(), starts_with("A").and(ends_with("string")))
}
}
@@ -0,0 +1,517 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::match_matrix::internal::{MatchMatrix, Requirements};
use crate::matchers::eq_matcher::eq;
use std::fmt::Debug;
/// Matches a container equal (in the sense of `==`) to `expected`.
///
/// This is similar to [`crate::matchers::eq`] except that an assertion failure
/// message generated from this matcher will include the missing and unexpected
/// items in the actual value, e.g.:
///
/// ```text
/// Expected container to equal [1, 2, 3]
/// but was: [1, 2, 4]
/// Missing: [3]
/// Unexpected: [4]
/// ```
///
/// The actual value must be a container such as a `&Vec`, an array, or a
/// dereferenced slice. More precisely, the actual value must
/// implement [`IntoIterator`] whose `Item` type implements
/// [`PartialEq<ExpectedT>`], where `ExpectedT` is the element type of the
/// expected value.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let vec = vec![1, 2, 3];
/// verify_that!(vec, container_eq([1, 2, 3]))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// *Performance note*: In the event of a mismatch leading to an assertion
/// failure, the construction of the lists of missing and unexpected values
/// uses a naive algorithm requiring time proportional to the product of the
/// sizes of the expected and actual values. This should therefore only be used
/// when the containers are small enough that this is not a problem.
pub fn container_eq<ExpectedContainerT>(
expected: ExpectedContainerT,
) -> ContainerEqMatcher<ExpectedContainerT>
where
ExpectedContainerT: Debug,
{
ContainerEqMatcher { expected }
}
#[derive(MatcherBase)]
pub struct ContainerEqMatcher<ExpectedContainerT> {
expected: ExpectedContainerT,
}
impl<ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT>
Matcher<ActualContainerT> for ContainerEqMatcher<ExpectedContainerT>
where
ActualElementT: for<'a> PartialEq<&'a ExpectedElementT> + Debug + Copy,
ActualContainerT: for<'a> PartialEq<&'a ExpectedContainerT> + Debug + Copy,
ExpectedElementT: Debug,
ExpectedContainerT: Debug,
ActualContainerT: IntoIterator<Item = ActualElementT>,
for<'a> &'a ExpectedContainerT: IntoIterator<Item = &'a ExpectedElementT>,
{
fn matches(&self, actual: ActualContainerT) -> MatcherResult {
(actual == &self.expected).into()
}
fn explain_match(&self, actual: ActualContainerT) -> Description {
build_explanation(self.get_missing_items(actual), self.get_unexpected_items(actual)).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
}
}
}
impl<ExpectedElementT, ExpectedContainerT> ContainerEqMatcher<ExpectedContainerT>
where
for<'a> &'a ExpectedContainerT: IntoIterator<Item = &'a ExpectedElementT>,
{
fn get_missing_items<ActualElementT, ActualContainerT>(
&self,
actual: ActualContainerT,
) -> Vec<&'_ ExpectedElementT>
where
ActualElementT: for<'a> PartialEq<&'a ExpectedElementT> + Copy,
ActualContainerT: for<'a> PartialEq<&'a ExpectedContainerT> + Copy,
ActualContainerT: IntoIterator<Item = ActualElementT>,
{
self.expected.into_iter().filter(|i| !actual.into_iter().any(|j| j == *i)).collect()
}
fn get_unexpected_items<ActualElementT, ActualContainerT>(
&self,
actual: ActualContainerT,
) -> Vec<ActualElementT>
where
ActualElementT: for<'a> PartialEq<&'a ExpectedElementT> + Copy,
ActualContainerT: for<'a> PartialEq<&'a ExpectedContainerT> + Copy,
ActualContainerT: IntoIterator<Item = ActualElementT>,
{
actual.into_iter().filter(|i| !self.expected.into_iter().any(|j| i == &j)).collect()
}
}
fn build_explanation<T: Debug, U: Debug>(missing: Vec<T>, unexpected: Vec<U>) -> String {
match (missing.len(), unexpected.len()) {
// TODO(b/261175849) add more data here (out of order elements, duplicated elements, etc...)
(0, 0) => "which contains all the elements".to_string(),
(0, 1) => format!("which contains the unexpected element {:?}", unexpected[0]),
(0, _) => format!("which contains the unexpected elements {unexpected:?}",),
(1, 0) => format!("which is missing the element {:?}", missing[0]),
(1, 1) => {
format!(
"which is missing the element {:?} and contains the unexpected element {:?}",
missing[0], unexpected[0]
)
}
(1, _) => {
format!(
"which is missing the element {:?} and contains the unexpected elements {unexpected:?}",
missing[0]
)
}
(_, 0) => format!("which is missing the elements {missing:?}"),
(_, 1) => {
format!(
"which is missing the elements {missing:?} and contains the unexpected element {:?}",
unexpected[0]
)
}
(_, _) => {
format!(
"which is missing the elements {missing:?} and contains the unexpected elements {unexpected:?}",
)
}
}
}
impl<ExpectedContainerT> ContainerEqMatcher<ExpectedContainerT> {
/// Match container equality, but ignoring element order.
pub fn ignore_order(self) -> IgnoringOrder<ExpectedContainerT> {
IgnoringOrder { expected: self.expected }
}
}
#[derive(MatcherBase)]
pub struct IgnoringOrder<ExpectedContainerT> {
expected: ExpectedContainerT,
}
/// Implements a matcher that ignores the relative order of the elements.
impl<ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT>
Matcher<ActualContainerT> for IgnoringOrder<ExpectedContainerT>
where
ActualElementT: Debug + Copy + for<'a> PartialEq<&'a ExpectedElementT>,
ActualContainerT: Debug + Copy + IntoIterator<Item = ActualElementT>,
ExpectedElementT: Debug,
for<'a> &'a ExpectedContainerT: IntoIterator<Item = &'a ExpectedElementT>,
{
fn matches(&self, actual: ActualContainerT) -> MatcherResult {
let expected: Vec<Box<dyn Matcher<ActualElementT>>> = self
.expected
.into_iter()
.map(|x| Box::new(eq(x)) as Box<dyn Matcher<ActualElementT>>)
.collect();
let match_matrix = MatchMatrix::generate(actual, &expected);
// TODO: investigate why Requirements::PerfectMatch and
// match_matrix.is_full_match is not doing what we expect
// here.
(match_matrix.is_match_for(Requirements::Subset)
&& match_matrix.is_match_for(Requirements::Superset))
.into()
}
fn explain_match(&self, actual: ActualContainerT) -> Description {
// We need to materialize the collections in order to have reliable iteration
// order when generating our reports.
let expected_items: Vec<&ExpectedElementT> = self.expected.into_iter().collect();
let actual_items: Vec<ActualElementT> = actual.into_iter().collect();
let expected_matchers: Vec<_> = expected_items
.iter()
.map(|&x| Box::new(eq(x)) as Box<dyn Matcher<ActualElementT>>)
.collect();
let match_matrix = MatchMatrix::generate(actual_items.iter().copied(), &expected_matchers);
let best_match = match_matrix.find_best_match();
// Since we are doing equality checks, we can generate a slightly less verbose
// message than BestMatch::get_explanation.
let matches = best_match.get_matches().map(|(actual_idx, expected_idx)|{
Description::new().text(
format!(
"Actual element {:?} at index {actual_idx} is equal to expected element at index {expected_idx}.",
actual_items[actual_idx],
))});
let unmatched_actual = best_match.get_unmatched_actual().map(|actual_idx| {
Description::new().text(
format!(
"Actual element {:?} at index {actual_idx} did not match any remaining expected element.",
actual_items[actual_idx],
))
});
let unmatched_expected =
best_match.get_unmatched_expected().into_iter().map(|expected_idx| {
Description::new().text(format!(
"Expected element {:?} at index {expected_idx} did not match any remaining actual element.",
expected_items[expected_idx]
))
});
Description::new()
.text("which does not have a perfect match. The best match found was:")
.collect(matches.chain(unmatched_actual).chain(unmatched_expected))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
Description::new()
.text(format!(
"{} all elements matching in any order:",
if matcher_result.into() { "contains" } else { "doesn't contain" },
))
.nested(
self.expected
.into_iter()
.map(|element| format!("{element:?}"))
.collect::<Description>()
.bullet_list(),
)
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::collections::HashSet;
#[test]
fn container_eq_returns_match_when_containers_match() -> Result<()> {
verify_that!(vec![1, 2, 3], container_eq(vec![1, 2, 3]))
}
#[test]
fn container_eq_matches_array_with_slice() -> Result<()> {
let value = &[1, 2, 3];
verify_that!(value, container_eq([1, 2, 3]))
}
#[test]
fn container_eq_matches_hash_set() -> Result<()> {
let value: HashSet<i32> = [1, 2, 3].into();
verify_that!(value, container_eq([1, 2, 3].into()))
}
#[test]
fn container_eq_full_error_message() -> Result<()> {
let result = verify_that!(vec![1, 3, 2], container_eq(vec![1, 2, 3]));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![1, 3, 2]
Expected: is equal to [1, 2, 3]
Actual: [1, 3, 2],
which contains all the elements
"
))))
)
}
#[test]
fn container_eq_returns_mismatch_when_elements_out_of_order() -> Result<()> {
verify_that!(
container_eq(vec![1, 2, 3]).explain_match(&vec![1, 3, 2]),
displays_as(eq("which contains all the elements"))
)
}
#[test]
fn container_eq_mismatch_shows_missing_elements_in_container() -> Result<()> {
verify_that!(
container_eq(vec![1, 2, 3]).explain_match(&vec![1, 2]),
displays_as(eq("which is missing the element 3"))
)
}
#[test]
fn container_eq_mismatch_shows_surplus_elements_in_container() -> Result<()> {
verify_that!(
container_eq(vec![1, 2]).explain_match(&vec![1, 2, 3]),
displays_as(eq("which contains the unexpected element 3"))
)
}
#[test]
fn container_eq_mismatch_shows_missing_and_surplus_elements_in_container() -> Result<()> {
verify_that!(
container_eq(vec![1, 2, 3]).explain_match(&vec![1, 2, 4]),
displays_as(eq("which is missing the element 3 and contains the unexpected element 4"))
)
}
#[test]
fn container_eq_mismatch_does_not_show_duplicated_element() -> Result<()> {
verify_that!(
container_eq(vec![1, 2, 3]).explain_match(&vec![1, 2, 3, 3]),
displays_as(eq("which contains all the elements"))
)
}
#[test]
fn container_eq_matches_owned_vec_with_array() -> Result<()> {
let vector = vec![123, 234];
verify_that!(vector, container_eq([123, 234]))
}
#[test]
fn container_eq_matches_owned_vec_of_owned_strings_with_slice_of_string_references(
) -> Result<()> {
let vector = vec!["A string".to_string(), "Another string".to_string()];
verify_that!(vector, container_eq(["A string", "Another string"]))
}
#[test]
fn container_eq_matches_owned_vec_of_owned_strings_with_shorter_slice_of_string_references(
) -> Result<()> {
let actual = vec!["A string".to_string(), "Another string".to_string()];
let matcher = container_eq(["A string"]);
let result = matcher.matches(&actual);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn container_eq_mismatch_with_slice_shows_missing_elements_in_container() -> Result<()> {
verify_that!(
container_eq([1, 2, 3]).explain_match(&vec![1, 2]),
displays_as(eq("which is missing the element 3"))
)
}
#[test]
fn container_eq_mismatch_with_str_slice_shows_missing_elements_in_container() -> Result<()> {
verify_that!(
container_eq(["A", "B", "C"]).explain_match(&vec!["A".to_string(), "B".to_string()]),
displays_as(eq("which is missing the element \"C\""))
)
}
#[test]
fn container_eq_mismatch_with_str_slice_shows_surplus_elements_in_container() -> Result<()> {
verify_that!(
container_eq(["A", "B"]).explain_match(&vec![
"A".to_string(),
"B".to_string(),
"C".to_string()
]),
displays_as(eq("which contains the unexpected element \"C\""))
)
}
#[test]
fn ignoring_order_match() -> Result<()> {
verify_that!(vec!["a", "b"], container_eq(["b", "a"]).ignore_order())
}
#[test]
fn ignoring_order_mismatch() -> Result<()> {
verify_that!(vec!["a", "b"], not(container_eq(["1", "2"]).ignore_order()))
}
#[test]
fn ignoring_order_mismatch_explain() -> Result<()> {
let expected_err = verify_that!(vec!["a", "b"], container_eq(["1", "2"]).ignore_order());
verify_that!(
expected_err,
err(displays_as(contains_substring(indoc!(
r#"
Value of: vec!["a", "b"]
Expected: contains all elements matching in any order:
* "1"
* "2"
Actual: ["a", "b"],
which does not have a perfect match. The best match found was:
Actual element "a" at index 0 did not match any remaining expected element.
Actual element "b" at index 1 did not match any remaining expected element.
Expected element "1" at index 0 did not match any remaining actual element.
Expected element "2" at index 1 did not match any remaining actual element.
"#
))))
)
}
#[test]
fn ignoring_order_unaccounted_extra_expected() -> Result<()> {
verify_that!(vec!["a", "b"], not(container_eq(["a", "b", "a"]).ignore_order()))
}
#[test]
fn ignoring_order_unaccounted_extra_expected_explain() -> Result<()> {
let expected_err =
verify_that!(vec!["a", "b"], container_eq(["a", "b", "a"]).ignore_order());
verify_that!(
expected_err,
err(displays_as(contains_substring(indoc!(
r#"
Value of: vec!["a", "b"]
Expected: contains all elements matching in any order:
* "a"
* "b"
* "a"
Actual: ["a", "b"],
which does not have a perfect match. The best match found was:
Actual element "a" at index 0 is equal to expected element at index 0.
Actual element "b" at index 1 is equal to expected element at index 1.
Expected element "a" at index 2 did not match any remaining actual element.
"#
))))
)
}
#[test]
fn ignoring_order_unaccounted_extra_actual() -> Result<()> {
verify_that!(vec!["a", "b", "a"], not(container_eq(["b", "a"]).ignore_order()))
}
#[test]
fn ignoring_order_unaccounted_extra_actual_explain() -> Result<()> {
let expected_err =
verify_that!(vec!["a", "b", "a"], container_eq(["b", "a"]).ignore_order());
verify_that!(
expected_err,
err(displays_as(contains_substring(indoc!(
r#"
Value of: vec!["a", "b", "a"]
Expected: contains all elements matching in any order:
* "b"
* "a"
Actual: ["a", "b", "a"],
which does not have a perfect match. The best match found was:
Actual element "a" at index 0 is equal to expected element at index 1.
Actual element "b" at index 1 is equal to expected element at index 0.
Actual element "a" at index 2 did not match any remaining expected element.
"#
))))
)
}
#[test]
fn ignoring_order_on_sets() -> Result<()> {
let mut actual = std::collections::HashSet::new();
actual.insert("b");
actual.insert("a");
actual.insert("c");
verify_that!(actual, container_eq(["c", "b", "a"]).ignore_order())
}
#[test]
fn ignoring_order_on_sets_explain() -> Result<()> {
let mut actual = std::collections::HashSet::new();
actual.insert("b");
actual.insert("a");
actual.insert("c");
let expected_err = verify_that!(actual, container_eq(["c", "a"]).ignore_order());
verify_that!(
expected_err,
err(displays_as(contains_regex(indoc!(
r#"
Value of: actual
Expected: contains all elements matching in any order:
\* "c"
\* "a"
Actual: \{"\w", "\w", "\w"\},
which does not have a perfect match. The best match found was:
Actual element "\w" at index \d is equal to expected element at index \d\.
Actual element "\w" at index \d is equal to expected element at index \d\.
Actual element "\w" at index \d did not match any remaining expected element\.
"#
))))
)
}
#[test]
fn ignoring_order_on_number_sets() -> Result<()> {
let mut actual = std::collections::HashSet::new();
actual.insert(1);
actual.insert(2);
actual.insert(3);
verify_that!(actual, container_eq([3, 2, 1]).ignore_order())
}
}
@@ -0,0 +1,279 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches an [`IntoIterator`] type whose elements contain a value matched by
/// `inner`.
///
/// By default, this matches a container with any number of elements matched
/// by `inner`. Use the method [`ContainsMatcher::times`] to constrain the
/// matched containers to a specific number of matching elements.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(["Some value"], contains(eq("Some value")))?; // Passes
/// verify_that!(vec!["Some value"], contains(eq(&"Some value")))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
/// verify_that!([] as [&String; 0], contains(eq("Some value")))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// verify_that!(["Some value"], contains(eq("Some other value")))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
pub fn contains<InnerMatcherT>(inner: InnerMatcherT) -> ContainsMatcher<InnerMatcherT> {
ContainsMatcher { inner, count: None }
}
/// A matcher which matches a container containing one or more elements a given
/// inner [`Matcher`] matches.
#[derive(MatcherBase)]
pub struct ContainsMatcher<InnerMatcherT> {
inner: InnerMatcherT,
count: Option<Box<dyn Matcher<usize>>>,
}
impl<InnerMatcherT> ContainsMatcher<InnerMatcherT> {
/// Configures this instance to match containers which contain a number of
/// matching items matched by `count`.
///
/// For example, to assert that exactly three matching items must be
/// present, use:
///
/// ```ignore
/// contains(...).times(eq(3))
/// ```
///
/// One can also use `times(eq(0))` to test for the *absence* of an item
/// matching the expected value.
pub fn times(mut self, count: impl Matcher<usize> + 'static) -> Self {
self.count = Some(Box::new(count));
self
}
}
// TODO(hovinen): Revisit the trait bounds to see whether this can be made more
// flexible. Namely, the following doesn't compile currently:
//
// let matcher = contains(eq(&42));
// let val = 42;
// let _ = matcher.matches(&vec![&val]);
//
// because val is dropped before matcher but the trait bound requires that
// the argument to matches outlive the matcher. It works fine if one defines
// val before matcher.
impl<T: Debug + Copy, InnerMatcherT: Matcher<T>, ContainerT: Debug + Copy> Matcher<ContainerT>
for ContainsMatcher<InnerMatcherT>
where
ContainerT: IntoIterator<Item = T>,
{
fn matches(&self, actual: ContainerT) -> MatcherResult {
if let Some(count) = &self.count {
count.matches(self.count_matches(actual))
} else {
for v in actual.into_iter() {
if self.inner.matches(v).into() {
return MatcherResult::Match;
}
}
MatcherResult::NoMatch
}
}
fn explain_match(&self, actual: ContainerT) -> Description {
let count = self.count_matches(actual);
match (count, &self.count) {
(_, Some(_)) => format!("which contains {count} matching elements").into(),
(0, None) => "which does not contain a matching element".into(),
(_, None) => "which contains a matching element".into(),
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match (matcher_result, &self.count) {
(MatcherResult::Match, Some(count)) => format!(
"contains n elements which {}\n where n {}",
self.inner.describe(MatcherResult::Match),
count.describe(MatcherResult::Match)
)
.into(),
(MatcherResult::NoMatch, Some(count)) => format!(
"doesn't contain n elements which {}\n where n {}",
self.inner.describe(MatcherResult::Match),
count.describe(MatcherResult::Match)
)
.into(),
(MatcherResult::Match, None) => format!(
"contains at least one element which {}",
self.inner.describe(MatcherResult::Match)
)
.into(),
(MatcherResult::NoMatch, None) => {
format!("contains no element which {}", self.inner.describe(MatcherResult::Match))
.into()
}
}
}
}
impl<InnerMatcherT> ContainsMatcher<InnerMatcherT> {
fn count_matches<T: Debug + Copy, ContainerT>(&self, actual: ContainerT) -> usize
where
ContainerT: IntoIterator<Item = T>,
InnerMatcherT: Matcher<T>,
{
let mut count = 0;
for v in actual.into_iter() {
if self.inner.matches(v).into() {
count += 1;
}
}
count
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
#[test]
fn contains_matches_singleton_slice_with_value() -> Result<()> {
let matcher = contains(eq(&1));
let result = matcher.matches(&vec![1]);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn contains_matches_singleton_vec_with_value() -> Result<()> {
let matcher = contains(eq(&1));
let result = matcher.matches(&vec![1]);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn contains_matches_two_element_slice_with_value() -> Result<()> {
let matcher = contains(eq(&1));
let result = matcher.matches(&[0, 1]);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn contains_does_not_match_singleton_slice_with_wrong_value() -> Result<()> {
let matcher = contains(eq(&1));
let result = matcher.matches(&[0]);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn contains_does_not_match_empty_slice() -> Result<()> {
let matcher = contains(eq(&1));
let result = matcher.matches(&[1; 0]);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn contains_matches_slice_with_repeated_value() -> Result<()> {
let matcher = contains(eq(&1)).times(eq(2));
let result = matcher.matches(&[1, 1]);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn contains_does_not_match_slice_with_too_few_of_value() -> Result<()> {
let matcher = contains(eq(&1)).times(eq(2));
let result = matcher.matches(&[0, 1]);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn contains_does_not_match_slice_with_too_many_of_value() -> Result<()> {
let matcher = contains(eq(&1)).times(eq(1));
let result = matcher.matches(&[1, 1]);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn contains_formats_without_multiplicity_by_default() -> Result<()> {
let matcher = contains(eq(&1));
verify_that!(
Matcher::<&Vec<i32>>::describe(&matcher, MatcherResult::Match),
displays_as(eq("contains at least one element which is equal to 1"))
)
}
#[test]
fn contains_formats_with_multiplicity_when_specified() -> Result<()> {
let matcher = contains(eq(&1)).times(eq(2));
verify_that!(
Matcher::<&Vec<i32>>::describe(&matcher, MatcherResult::Match),
displays_as(eq("contains n elements which is equal to 1\n where n is equal to 2"))
)
}
#[test]
fn contains_mismatch_shows_number_of_times_element_was_found() -> Result<()> {
verify_that!(
contains(eq(&3)).times(eq(1)).explain_match(&vec![1, 2, 3, 3]),
displays_as(eq("which contains 2 matching elements"))
)
}
#[test]
fn contains_mismatch_shows_when_matches() -> Result<()> {
verify_that!(
contains(eq(&3)).explain_match(&vec![1, 2, 3, 3]),
displays_as(eq("which contains a matching element"))
)
}
#[test]
fn contains_mismatch_shows_when_no_matches() -> Result<()> {
verify_that!(
contains(eq(&3)).explain_match(&vec![1, 2]),
displays_as(eq("which does not contain a matching element"))
)
}
}
@@ -0,0 +1,137 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use regex::Regex;
use std::fmt::Debug;
use std::ops::Deref;
/// Matches a string containing a substring which matches the given regular
/// expression.
///
/// Both the actual value and the expected regular expression may be either a
/// `String` or a string reference.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass_1() -> Result<()> {
/// verify_that!("Some value", contains_regex("S.*e"))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!("Another value", contains_regex("Some"))?; // Fails
/// # Ok(())
/// # }
/// # fn should_pass_2() -> Result<()> {
/// verify_that!("Some value".to_string(), contains_regex("v.*e"))?; // Passes
/// verify_that!("Some value", contains_regex("v.*e".to_string()))?; // Passes
/// # Ok(())
/// # }
/// # should_pass_1().unwrap();
/// # should_fail().unwrap_err();
/// # should_pass_2().unwrap();
/// ```
///
/// Panics if the given `pattern` is not a syntactically valid regular
/// expression.
#[track_caller]
pub fn contains_regex<PatternT: Deref<Target = str>>(pattern: PatternT) -> ContainsRegexMatcher {
ContainsRegexMatcher { regex: Regex::new(pattern.deref()).unwrap() }
}
/// A matcher matching a string-like type containing a substring matching a
/// given regular expression.
///
/// Intended only to be used from the function [`contains_regex`] only.
/// Should not be referenced by code outside this library.
#[derive(MatcherBase)]
pub struct ContainsRegexMatcher {
regex: Regex,
}
impl<ActualT: AsRef<str> + Debug + Copy> Matcher<ActualT> for ContainsRegexMatcher {
fn matches(&self, actual: ActualT) -> MatcherResult {
self.regex.is_match(actual.as_ref()).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("contains the regular expression {:#?}", self.regex.as_str()).into()
}
MatcherResult::NoMatch => {
format!("doesn't contain the regular expression {:#?}", self.regex.as_str()).into()
}
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
#[test]
fn contains_regex_matches_string_reference_with_pattern() -> Result<()> {
let matcher = contains_regex("S.*val");
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn contains_regex_does_not_match_string_without_pattern() -> Result<()> {
let matcher = contains_regex("Another");
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn contains_regex_matches_owned_string_with_pattern() -> Result<()> {
let matcher = contains_regex("value");
let result = matcher.matches(&"Some value".to_string());
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn contains_regex_matches_string_reference_with_owned_string() -> Result<()> {
let matcher = contains_regex("value");
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn verify_that_works_with_owned_string() -> Result<()> {
verify_that!("Some value".to_string(), contains_regex("value"))
}
#[test]
fn contains_regex_displays_quoted_debug_of_pattern() -> Result<()> {
let matcher = contains_regex("\n");
verify_that!(
Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("contains the regular expression \"\\n\""))
)
}
}
@@ -0,0 +1,100 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::{fmt::Debug, ops::Deref};
/// Dereferences the `actual` value and verifies that the returned reference
/// matches the `inner` matcher.
///
/// ```
/// # use googletest::{matchers::{derefs_to, eq}, verify_that};
/// verify_that!(Box::new(123), derefs_to(eq(&123)))
/// # .unwrap()
/// ```
pub fn derefs_to<Inner>(inner: Inner) -> DerefsTo<Inner> {
DerefsTo { inner }
}
/// A matcher which derefs a value and verifies that the result matches the
/// `inner` matcher.
///
/// See [`derefs_to`].
#[derive(MatcherBase)]
pub struct DerefsTo<InnerT> {
pub(crate) inner: InnerT,
}
impl<'a, ActualT, ExpectedT, Inner> Matcher<&'a ActualT> for DerefsTo<Inner>
where
ActualT: Deref<Target = ExpectedT> + Debug,
ExpectedT: Copy + Debug + 'a,
Inner: Matcher<&'a ExpectedT>,
{
fn matches(&self, actual: &'a ActualT) -> MatcherResult {
self.inner.matches(actual.deref())
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
self.inner.describe(matcher_result)
}
fn explain_match(&self, actual: &'a ActualT) -> Description {
self.inner.explain_match(actual.deref())
}
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn deref_to_matches_box_of_int_with_int() -> Result<()> {
let actual = Box::new(123);
verify_that!(actual, derefs_to(eq(&123)))
}
#[test]
fn deref_to_matches_rc_of_int_with_int() -> Result<()> {
verify_that!(Rc::new(123), derefs_to(eq(&123)))
}
#[test]
fn deref_to_combines_with_points_to_for_copy() -> Result<()> {
verify_that!(Rc::new(123), derefs_to(points_to(eq(123))))
}
#[test]
fn match_explanation_references_actual_value() -> Result<()> {
let actual = Box::new(1);
let result = verify_that!(actual, derefs_to(eq(&0)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Actual: 1,
which isn't equal to 0
"
))))
)
}
}
@@ -0,0 +1,152 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module.
#![doc(hidden)]
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matcher created by [`Matcher::or`] and [`any!`].
///
/// Both [`Matcher::or`] and [`any!`] nest on m1. In other words,
/// both `x.or(y).or(z)` and `any![x, y, z]` produce:
/// ```ignore
/// DisjunctionMatcher {
/// m1: DisjunctionMatcher {
/// m1: x, m2: y
/// },
/// m2: z
/// }
/// ```
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
#[derive(MatcherBase)]
pub struct DisjunctionMatcher<M1, M2> {
m1: M1,
m2: M2,
}
impl<M1, M2> DisjunctionMatcher<M1, M2> {
pub fn new(m1: M1, m2: M2) -> Self {
Self { m1, m2 }
}
}
impl<T: Debug + Copy, M1: Matcher<T>, M2: Matcher<T>> Matcher<T> for DisjunctionMatcher<M1, M2> {
fn matches(&self, actual: T) -> MatcherResult {
match (self.m1.matches(actual), self.m2.matches(actual)) {
(MatcherResult::NoMatch, MatcherResult::NoMatch) => MatcherResult::NoMatch,
_ => MatcherResult::Match,
}
}
fn explain_match(&self, actual: T) -> Description {
match (self.m1.matches(actual), self.m2.matches(actual)) {
(MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual),
(MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual),
(_, _) => {
let m1_description = self.m1.explain_match(actual);
if m1_description.is_disjunction_description() {
m1_description.nested(self.m2.explain_match(actual))
} else {
Description::new()
.bullet_list()
.collect([m1_description, self.m2.explain_match(actual)])
.disjunction_description()
}
}
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
let m1_description = self.m1.describe(matcher_result);
if m1_description.is_disjunction_description() {
m1_description.push_in_last_nested(self.m2.describe(matcher_result))
} else {
let header = if matcher_result.into() {
"has at least one of the following properties:"
} else {
"has all of the following properties:"
};
Description::new()
.text(header)
.nested(
Description::new()
.bullet_list()
.collect([m1_description, self.m2.describe(matcher_result)]),
)
.disjunction_description()
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn or_true_true_matches() -> Result<()> {
verify_that!(1, anything().or(anything()))
}
#[test]
fn or_true_false_matches() -> Result<()> {
verify_that!(1, anything().or(not(anything())))
}
#[test]
fn or_false_true_matches() -> Result<()> {
verify_that!(1, not(anything()).or(anything()))
}
#[test]
fn or_false_false_does_not_match() -> Result<()> {
let result = verify_that!(1, not(anything()).or(not(anything())));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 1
Expected: has at least one of the following properties:
* never matches
* never matches
Actual: 1,
* which is anything
* which is anything
"
))))
)
}
#[test]
fn chained_or_matches() -> Result<()> {
verify_that!(10, eq(1).or(eq(5)).or(ge(9)))
}
#[test]
fn works_with_str_slices() -> Result<()> {
verify_that!("A string", ends_with("A").or(ends_with("string")))
}
#[test]
fn works_with_owned_strings() -> Result<()> {
verify_that!("A string".to_string(), ends_with("A").or(ends_with("string")))
}
}
@@ -0,0 +1,124 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::fmt::{Debug, Display};
/// Matches the string representation of types that implement `Display`.
///
/// ```ignore
/// let result: impl Display = ...;
/// verify_that!(result, displays_as(eq(format!("{}", result))))?;
/// ```
pub fn displays_as<InnerMatcher: for<'a> Matcher<&'a str>>(
inner: InnerMatcher,
) -> DisplayMatcher<InnerMatcher> {
DisplayMatcher { inner }
}
#[derive(MatcherBase)]
pub struct DisplayMatcher<InnerMatcher> {
inner: InnerMatcher,
}
impl<T: Debug + Display + Copy, InnerMatcher: for<'a> Matcher<&'a str>> Matcher<T>
for DisplayMatcher<InnerMatcher>
{
fn matches(&self, actual: T) -> MatcherResult {
self.inner.matches(&format!("{actual}"))
}
fn explain_match(&self, actual: T) -> Description {
format!(
"which displays as {:?} {}",
actual.to_string(),
self.inner.explain_match(&format!("{actual}"))
)
.into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("displays as a string which {}", self.inner.describe(MatcherResult::Match))
.into()
}
MatcherResult::NoMatch => format!(
"doesn't display as a string which {}",
self.inner.describe(MatcherResult::Match)
)
.into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::fmt::{Debug, Display, Error, Formatter};
#[test]
fn display_matches_i32() -> Result<()> {
let value = 32;
verify_that!(value, displays_as(eq("32")))?;
Ok(())
}
#[test]
fn display_matches_str() -> Result<()> {
let value = "32";
verify_that!(value, displays_as(eq("32")))?;
Ok(())
}
#[test]
fn display_matches_struct() -> Result<()> {
#[allow(dead_code)]
#[derive(Debug)]
struct Struct {
a: i32,
b: i64,
}
impl Display for Struct {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), Error> {
write!(f, "{self:?}")
}
}
verify_that!(Struct { a: 123, b: 321 }, displays_as(eq("Struct { a: 123, b: 321 }")))?;
Ok(())
}
#[test]
fn display_displays_error_message_with_explanation_from_inner_matcher() -> Result<()> {
let result = verify_that!("123\n234", displays_as(eq("123\n345")));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Actual: \"123\\n234\",
which displays as \"123\\n234\" which isn't equal to \"123\\n345\"
Difference(-actual / +expected):
123
-234
+345
"
))))
)
}
}
@@ -0,0 +1,231 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::fmt::Debug;
/// Matches a container all of whose elements are matched by the matcher
/// `inner`.
///
/// `T` must implement [`IntoIterator`]. This
/// includes `&Vec`, arrays, and slices.
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashSet;
/// # fn should_pass_1() -> Result<()> {
/// let value = vec![1, 2, 3];
/// verify_that!(value, each(gt(&0)))?; // Passes
/// let array_value = [1, 2, 3];
/// verify_that!(array_value, each(gt(0)))?; // Passes
/// let slice_value = &[1, 2, 3];
/// verify_that!(slice_value, each(gt(&0)))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// # let value = vec![1, 2, 3];
/// verify_that!(value, each(lt(&2)))?; // Fails: 2 and 3 are not less than 2
/// # Ok(())
/// # }
///
/// # fn should_pass_2() -> Result<()> {
/// let value: HashSet<i32> = [1, 2, 3].into();
/// verify_that!(value, each(gt(&0)))?; // Passes
/// # Ok(())
/// # }
/// # should_pass_1().unwrap();
/// # should_fail().unwrap_err();
/// # should_pass_2().unwrap();
/// ```
pub fn each<MatcherT>(inner: MatcherT) -> EachMatcher<MatcherT> {
EachMatcher { inner }
}
#[derive(MatcherBase)]
pub struct EachMatcher<MatcherT> {
inner: MatcherT,
}
impl<ElementT: Debug + Copy, ActualT: Debug + Copy, MatcherT> Matcher<ActualT>
for EachMatcher<MatcherT>
where
ActualT: IntoIterator<Item = ElementT>,
MatcherT: Matcher<ElementT>,
{
fn matches(&self, actual: ActualT) -> MatcherResult {
for element in actual {
if self.inner.matches(element).is_no_match() {
return MatcherResult::NoMatch;
}
}
MatcherResult::Match
}
fn explain_match(&self, actual: ActualT) -> Description {
let mut non_matching_elements = Vec::new();
for (index, element) in actual.into_iter().enumerate() {
if self.inner.matches(element).is_no_match() {
non_matching_elements.push((index, element, self.inner.explain_match(element)));
}
}
if non_matching_elements.is_empty() {
return format!("whose each element {}", self.inner.describe(MatcherResult::Match))
.into();
}
if non_matching_elements.len() == 1 {
let (idx, element, explanation) = non_matching_elements.remove(0);
return format!("whose element #{idx} is {element:?}, {explanation}").into();
}
let failed_indexes = non_matching_elements
.iter()
.map(|&(idx, _, _)| format!("#{idx}"))
.collect::<Vec<_>>()
.join(", ");
let element_explanations = non_matching_elements
.iter()
.map(|&(_, element, ref explanation)| format!("{element:?}, {explanation}"))
.collect::<Description>()
.indent();
format!("whose elements {failed_indexes} don't match\n{element_explanations}").into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("only contains elements that {}", self.inner.describe(MatcherResult::Match))
.into()
}
MatcherResult::NoMatch => {
format!("contains no element that {}", self.inner.describe(MatcherResult::Match))
.into()
}
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::collections::HashSet;
#[test]
fn each_matches_empty_vec() -> Result<()> {
let value: Vec<i32> = vec![];
verify_that!(value, each(gt(&0)))
}
#[test]
fn each_matches_vec_with_one_element() -> Result<()> {
let value = vec![1];
verify_that!(value, each(gt(&0)))
}
#[test]
fn each_matches_vec_with_two_elements() -> Result<()> {
let value = vec![1, 2];
verify_that!(value, each(gt(&0)))
}
#[test]
fn each_matches_slice_with_one_element() -> Result<()> {
let value = &[1];
verify_that!(*value, each(gt(0)))
}
#[test]
fn each_matches_hash_set_with_one_element() -> Result<()> {
let value: HashSet<i32> = [1].into();
verify_that!(value, each(gt(&0)))
}
#[test]
fn each_does_not_match_when_first_element_does_not_match() -> Result<()> {
let value = vec![0];
verify_that!(value, not(each(gt(&1))))
}
#[test]
fn each_does_not_match_when_second_element_does_not_match() -> Result<()> {
let value = vec![2, 0];
verify_that!(value, not(each(gt(&1))))
}
#[test]
fn each_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
let result = verify_that!(vec![0, 2, 3], each(gt(&0)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![0, 2, 3]
Expected: only contains elements that is greater than 0
Actual: [0, 2, 3],
whose element #0 is 0, which is less than or equal to 0"
))))
)
}
#[test]
fn each_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
let result = verify_that!(vec![1, 0, 3], each(gt(&0)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![1, 0, 3]
Expected: only contains elements that is greater than 0
Actual: [1, 0, 3],
whose element #1 is 0, which is less than or equal to 0"
))))
)
}
#[test]
fn each_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
let result = verify_that!(vec![0, 1, 3], each(gt(&1)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![0, 1, 3]
Expected: only contains elements that is greater than 1
Actual: [0, 1, 3],
whose elements #0, #1 don't match
0, which is less than or equal to 1
1, which is less than or equal to 1"
))))
)
}
#[test]
fn each_shows_inner_explanation() -> Result<()> {
let result = verify_that!(vec![vec![1, 2], vec![1]], each(each(eq(&1))));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Expected: only contains elements that only contains elements that is equal to 1
Actual: [[1, 2], [1]],
whose element #0 is [1, 2], whose element #1 is 2, which isn't equal to 1"
))))
)
}
}
@@ -0,0 +1,185 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module; the declarative
// macro is documented in the matchers module.
#![doc(hidden)]
/// Matches a container's elements to each matcher in order.
///
/// This macro produces a matcher against a container. It takes as arguments a
/// sequence of matchers each of which should respectively match the
/// corresponding element of the actual value.
///
/// ```
/// # use googletest::prelude::*;
/// verify_that!(vec![1, 2, 3], elements_are![eq(&1), anything(), gt(&0).and(lt(&123))])
/// # .unwrap();
/// ```
///
/// The actual value must be a container such as a `&Vec`, an array, or a slice.
/// More precisely, the actual value must implement [`IntoIterator`].
///
/// ```
/// # use googletest::prelude::*;
/// let vector = vec![1, 2, 3];
/// let slice = vector.as_slice();
/// verify_that!(slice, elements_are![eq(&1), anything(), gt(&0).and(lt(&123))])
/// # .unwrap();
/// ```
///
/// This can also be omitted in [`verify_that!`] macros and replaced with square
/// brackets.
///
/// ```
/// # use googletest::prelude::*;
/// verify_that!(vec![1, 2], [eq(&1), eq(&2)])
/// # .unwrap();
/// ```
///
/// Note: This behavior is only possible in [`verify_that!`] macros. In any
/// other cases, it is still necessary to use the
/// [`elements_are!`][crate::matchers::elements_are] macro.
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// verify_that!(vec![vec![1,2], vec![3]], [[eq(&1), eq(&2)], [eq(&3)]])
/// # .unwrap();
/// ```
///
/// Use this instead:
/// ```
/// # use googletest::prelude::*;
/// verify_that!(vec![vec![1,2], vec![3]], [elements_are![eq(&1), eq(&2)], elements_are![eq(&3)]])
/// # .unwrap();
/// ```
///
/// If an inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
///
/// verify_that!(vec![1,2,3], elements_are![&1, lt(&1000), gt(&1)])
/// # .unwrap();
/// ```
///
/// Do not use this with unordered containers, since that will lead to flaky
/// tests. Use
/// [`unordered_elements_are!`][crate::matchers::unordered_elements_are]
/// instead.
///
/// [`IntoIterator`]: std::iter::IntoIterator
/// [`Iterator`]: std::iter::Iterator
/// [`Iterator::collect`]: std::iter::Iterator::collect
/// [`Vec`]: std::vec::Vec
#[macro_export]
#[doc(hidden)]
macro_rules! __elements_are {
($($matcher:expr),* $(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::ElementsAre::new(
vec![$(Box::new(
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
$matcher
)
)),*])
}}
}
/// Module for use only by the procedural macros in this module.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
pub mod internal {
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::zipped_iterator::zip;
use std::fmt::Debug;
/// This struct is meant to be used only by the macro `elements_are!`.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
#[derive(MatcherBase)]
pub struct ElementsAre<'a, T: Debug + Copy> {
elements: Vec<Box<dyn Matcher<T> + 'a>>,
}
impl<'a, T: Debug + Copy> ElementsAre<'a, T> {
/// Factory only intended for use in the macro `elements_are!`.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
pub fn new(elements: Vec<Box<dyn Matcher<T> + 'a>>) -> Self {
Self { elements }
}
}
impl<T: Debug + Copy, ContainerT: Debug + Copy> Matcher<ContainerT> for ElementsAre<'_, T>
where
ContainerT: IntoIterator<Item = T>,
{
fn matches(&self, actual: ContainerT) -> MatcherResult {
let mut zipped_iterator = zip(actual.into_iter(), self.elements.iter());
for (a, e) in zipped_iterator.by_ref() {
if e.matches(a).is_no_match() {
return MatcherResult::NoMatch;
}
}
if !zipped_iterator.has_size_mismatch() {
MatcherResult::Match
} else {
MatcherResult::NoMatch
}
}
fn explain_match(&self, actual: ContainerT) -> Description {
let actual_iterator = actual.into_iter();
let mut zipped_iterator = zip(actual_iterator, self.elements.iter());
let mut mismatches = Vec::new();
for (idx, (a, e)) in zipped_iterator.by_ref().enumerate() {
if e.matches(a).is_no_match() {
mismatches.push(format!("element #{idx} is {a:?}, {}", e.explain_match(a)));
}
}
if mismatches.is_empty() {
if !zipped_iterator.has_size_mismatch() {
"whose elements all match".into()
} else {
format!("whose size is {}", zipped_iterator.left_size()).into()
}
} else if mismatches.len() == 1 {
let mismatches = mismatches.into_iter().collect::<Description>();
format!("where {mismatches}").into()
} else {
let mismatches = mismatches.into_iter().collect::<Description>();
format!("where:\n{}", mismatches.bullet_list().indent()).into()
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"{} elements:\n{}",
if matcher_result.into() { "has" } else { "doesn't have" },
&self
.elements
.iter()
.map(|matcher| matcher.describe(MatcherResult::Match))
.collect::<Description>()
.enumerate()
.indent()
)
.into()
}
}
}
@@ -0,0 +1,90 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches an empty container.
///
/// `T` can be any container that implements `IntoIterator`. For instance, `T`
/// can be the reference of a common container like `&Vec` and
/// [`&HashSet`][std::collections::HashSet].
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashSet;
/// # fn should_pass() -> Result<()> {
/// let value: Vec<i32> = vec![];
/// verify_that!(value, is_empty())?;
/// let value: HashSet<i32> = HashSet::new();
/// verify_that!(value, is_empty())?;
/// let value: &[u32] = &[];
/// verify_that!(value, is_empty())?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
pub fn is_empty() -> EmptyMatcher {
EmptyMatcher
}
#[derive(MatcherBase)]
pub struct EmptyMatcher;
impl<T: Debug + Copy> Matcher<T> for EmptyMatcher
where
T: IntoIterator,
{
fn matches(&self, actual: T) -> MatcherResult {
actual.into_iter().next().is_none().into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
if matcher_result.into() { "is empty" } else { "isn't empty" }.into()
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use std::collections::HashSet;
#[test]
fn empty_matcher_match_empty_vec() -> Result<()> {
let value: Vec<i32> = vec![];
verify_that!(value, is_empty())
}
#[test]
fn empty_matcher_does_not_match_empty_vec() -> Result<()> {
let value = vec![1, 2, 3];
verify_that!(value, not(is_empty()))
}
#[test]
fn empty_matcher_matches_empty_slice() -> Result<()> {
let value: &[i32] = &[];
verify_that!(value, is_empty())
}
#[test]
fn empty_matcher_matches_empty_hash_set() -> Result<()> {
let value: HashSet<i32> = HashSet::new();
verify_that!(value, is_empty())
}
}
@@ -0,0 +1,422 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::edit_distance;
use crate::matcher_support::summarize_diff::create_diff;
use std::fmt::Debug;
/// Matches a value equal (in the sense of `==`) to `expected`.
///
/// The type of `expected` must implement the [`PartialEq`] trait so that the
/// expected and actual values can be compared.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(123, eq(123))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!(123, eq(234))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
/// `expected` to `actual` must be comparable with one another via the
/// [`PartialEq`] trait. In most cases, this means that they must be of the same
/// type. However, there are a few cases where different but closely related
/// types are comparable, for example `String` with `&str`.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(String::from("Some value"), eq("Some value"))?; // Passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// In most cases however, one must convert one of the arguments explicitly.
/// This can be surprising when comparing integer types or references.
///
/// ```compile_fail
/// verify_that!(123u32, eq(123u64))?; // Does not compile
/// verify_that!(123u32 as u64, eq(123u64))?; // Passes
/// ```
///
/// ```ignore
/// let actual: &T = ...;
/// let expected: T = T{...};
/// verify_that(actual, eq(expected))?; // Does not compile
/// verify_that(actual, eq(&expected))?; // Compiles
/// ```
///
/// When matching with string types (`&str` and `String`), one can set more
/// options on how equality is checked through the
/// [`StrMatcherConfigurator`][crate::matchers::str_matcher::StrMatcherConfigurator]
/// extension trait, which is implemented for this matcher.
pub fn eq<T>(expected: T) -> EqMatcher<T> {
EqMatcher { expected }
}
/// A matcher which matches a value equal to `expected`.
///
/// See [`eq`].
#[derive(MatcherBase)]
pub struct EqMatcher<T> {
pub(crate) expected: T,
}
impl<T: Debug, A: Debug + Copy + PartialEq<T>> Matcher<A> for EqMatcher<T> {
fn matches(&self, actual: A) -> MatcherResult {
(actual == self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
}
}
fn explain_match(&self, actual: A) -> Description {
let expected_debug = format!("{:#?}", self.expected);
let actual_debug = format!("{actual:#?}");
let description = Matcher::<A>::describe(self, self.matches(actual));
let diff = if is_multiline_string_debug(&actual_debug)
&& is_multiline_string_debug(&expected_debug)
{
create_diff(
// The two calls below return None if and only if the strings expected_debug
// respectively actual_debug are not enclosed in ". The calls to
// is_multiline_string_debug above ensure that they are. So the calls cannot
// actually return None and unwrap() should not panic.
&to_display_output(&actual_debug).unwrap(),
&to_display_output(&expected_debug).unwrap(),
edit_distance::Mode::Exact,
)
} else {
create_diff(&actual_debug, &expected_debug, edit_distance::Mode::Exact)
};
if diff.is_empty() {
format!("which {description}").into()
} else {
format!("which {description}\n\n{diff}").into()
}
}
}
fn is_multiline_string_debug(string: &str) -> bool {
string.starts_with('"')
&& string.ends_with('"')
&& !string.contains('\n')
&& string.contains("\\n")
}
fn to_display_output(string: &str) -> Option<String> {
Some(string.strip_prefix('"')?.strip_suffix('"')?.split("\\n").collect::<Vec<_>>().join("\n"))
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn eq_matches_string_reference_with_string_reference() -> Result<()> {
verify_that!("A string", eq("A string"))
}
#[test]
fn eq_matches_owned_string_with_string_reference() -> Result<()> {
let value = "A string".to_string();
verify_that!(value, eq("A string"))
}
#[test]
fn eq_matches_owned_string_reference_with_string_reference() -> Result<()> {
let value = "A string".to_string();
verify_that!(&value, eq("A string"))
}
#[test]
fn eq_matches_i32_with_i32() -> Result<()> {
verify_that!(123, eq(123))
}
#[test]
fn eq_struct_debug_diff() -> Result<()> {
#[derive(Debug, PartialEq)]
struct Strukt {
int: i32,
string: String,
}
let result = verify_that!(
Strukt { int: 123, string: "something".into() },
eq(&Strukt { int: 321, string: "someone".into() })
);
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
Actual: Strukt { int: 123, string: \"something\" },
which isn't equal to Strukt { int: 321, string: \"someone\" }
Difference(-actual / +expected):
Strukt {
- int: 123,
+ int: 321,
- string: \"something\",
+ string: \"someone\",
}
"})))
)
}
#[test]
fn eq_vec_debug_diff() -> Result<()> {
let result = verify_that!(vec![1, 2, 3], eq(&vec![1, 3, 4]));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
Value of: vec![1, 2, 3]
Expected: is equal to [1, 3, 4]
Actual: [1, 2, 3],
which isn't equal to [1, 3, 4]
Difference(-actual / +expected):
[
1,
- 2,
3,
+ 4,
]
"})))
)
}
#[test]
fn eq_vec_debug_diff_length_mismatch() -> Result<()> {
let result = verify_that!(vec![1, 2, 3, 4, 5], eq(&vec![1, 3, 5]));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
Value of: vec![1, 2, 3, 4, 5]
Expected: is equal to [1, 3, 5]
Actual: [1, 2, 3, 4, 5],
which isn't equal to [1, 3, 5]
Difference(-actual / +expected):
[
1,
- 2,
3,
- 4,
5,
]
"})))
)
}
#[test]
fn eq_debug_diff_common_lines_omitted() -> Result<()> {
let result = verify_that!((1..50).collect::<Vec<_>>(), eq(&(3..52).collect::<Vec<_>>()));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
],
which isn't equal to [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
Difference(-actual / +expected):
[
- 1,
- 2,
3,
4,
<---- 43 common lines omitted ---->
48,
49,
+ 50,
+ 51,
]"})))
)
}
#[test]
fn eq_debug_diff_5_common_lines_not_omitted() -> Result<()> {
let result = verify_that!((1..8).collect::<Vec<_>>(), eq(&(3..10).collect::<Vec<_>>()));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
Actual: [1, 2, 3, 4, 5, 6, 7],
which isn't equal to [3, 4, 5, 6, 7, 8, 9]
Difference(-actual / +expected):
[
- 1,
- 2,
3,
4,
5,
6,
7,
+ 8,
+ 9,
]"})))
)
}
#[test]
fn eq_debug_diff_start_common_lines_omitted() -> Result<()> {
let result = verify_that!((1..50).collect::<Vec<_>>(), eq(&(1..52).collect::<Vec<_>>()));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
],
which isn't equal to [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
Difference(-actual / +expected):
[
1,
<---- 46 common lines omitted ---->
48,
49,
+ 50,
+ 51,
]"})))
)
}
#[test]
fn eq_debug_diff_end_common_lines_omitted() -> Result<()> {
let result = verify_that!((1..52).collect::<Vec<_>>(), eq(&(3..52).collect::<Vec<_>>()));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
],
which isn't equal to [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
Difference(-actual / +expected):
[
- 1,
- 2,
3,
4,
<---- 46 common lines omitted ---->
51,
]"})))
)
}
#[test]
fn eq_multi_line_string_debug_diff() -> Result<()> {
let result = verify_that!("One\nTwo\nThree", eq("One\nSix\nThree"));
// TODO: b/257454450 - Make this more useful, by potentially unescaping the
// line return.
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
r#"
Value of: "One\nTwo\nThree"
Expected: is equal to "One\nSix\nThree"
Actual: "One\nTwo\nThree",
which isn't equal to "One\nSix\nThree"
"#})))
)
}
#[test]
fn match_explanation_contains_diff_of_strings_if_more_than_one_line() -> Result<()> {
let result = verify_that!(
indoc!(
"
First line
Second line
Third line
"
),
eq(indoc!(
"
First line
Second lines
Third line
"
))
);
verify_that!(
result,
err(displays_as(contains_substring(
"\
First line
-Second line
+Second lines
Third line"
)))
)
}
#[test]
fn match_explanation_does_not_show_diff_if_actual_value_is_single_line() -> Result<()> {
let result = verify_that!(
"First line",
eq(indoc!(
"
First line
Second line
Third line
"
))
);
verify_that!(
result,
err(displays_as(not(contains_substring("Difference(-actual / +expected):"))))
)
}
#[test]
fn match_explanation_does_not_show_diff_if_expected_value_is_single_line() -> Result<()> {
let result = verify_that!(
indoc!(
"
First line
Second line
Third line
"
),
eq("First line")
);
verify_that!(
result,
err(displays_as(not(contains_substring("Difference(-actual / +expected):"))))
)
}
}
@@ -0,0 +1,282 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a `Result` containing `Err` with a value matched by `inner`.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> googletest::Result<()> {
/// verify_that!(Err::<(), _>("Some error"), err(eq("Some error")))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> googletest::Result<()> {
/// verify_that!(Ok::<_, &str>("A value"), err(eq("A value")))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_2() -> googletest::Result<()> {
/// verify_that!(Err::<(), _>("Some error"), err(eq("Some other error")))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
pub fn err<Inner>(inner: Inner) -> ErrMatcher<Inner> {
ErrMatcher { inner }
}
#[derive(MatcherBase)]
pub struct ErrMatcher<InnerMatcherT> {
inner: InnerMatcherT,
}
impl<T: Debug + Copy, E: Debug + Copy, InnerMatcherT: Matcher<E>> Matcher<std::result::Result<T, E>>
for ErrMatcher<InnerMatcherT>
{
fn matches(&self, actual: std::result::Result<T, E>) -> MatcherResult {
actual.err().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
}
fn explain_match(&self, actual: std::result::Result<T, E>) -> Description {
match actual {
Err(e) => {
Description::new().text("which is an error").nested(self.inner.explain_match(e))
}
Ok(_) => "which is a success".into(),
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("is an error which {}", self.inner.describe(MatcherResult::Match)).into()
}
MatcherResult::NoMatch => format!(
"is a success or is an error containing a value which {}",
self.inner.describe(MatcherResult::NoMatch)
)
.into(),
}
}
}
impl<'a, T: Debug, E: Debug, InnerMatcherT: Matcher<&'a E>> Matcher<&'a std::result::Result<T, E>>
for ErrMatcher<InnerMatcherT>
{
fn matches(&self, actual: &'a std::result::Result<T, E>) -> MatcherResult {
actual.as_ref().err().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
}
fn explain_match(&self, actual: &'a std::result::Result<T, E>) -> Description {
match actual {
Err(e) => {
Description::new().text("which is an error").nested(self.inner.explain_match(e))
}
Ok(_) => "which is a success".into(),
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("is an error which {}", self.inner.describe(MatcherResult::Match)).into()
}
MatcherResult::NoMatch => format!(
"is a success or is an error containing a value which {}",
self.inner.describe(MatcherResult::NoMatch)
)
.into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn err_matches_result_with_err_value() -> Result<()> {
let matcher = err(eq(1));
let value: std::result::Result<i32, i32> = Err(1);
let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn err_does_not_match_result_with_wrong_err_value() -> Result<()> {
let matcher = err(eq(1));
let value: std::result::Result<i32, i32> = Err(0);
let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn err_does_not_match_result_with_ok() -> Result<()> {
let matcher = err(eq(1));
let value: std::result::Result<i32, i32> = Ok(1);
let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn err_matches_result_with_err_value_by_ref() -> Result<()> {
let value: std::result::Result<String, String> = Err("123".to_string());
verify_that!(value, err(eq("123")))
}
#[test]
fn err_does_not_match_result_with_wrong_err_value_by_ref() -> Result<()> {
let value: std::result::Result<String, String> = Err("321".to_string());
verify_that!(value, not(err(eq("123"))))
}
#[test]
fn err_does_not_match_result_with_ok_by_ref() -> Result<()> {
let value: std::result::Result<String, String> = Ok("123".to_string());
verify_that!(value, not(err(eq("123"))))
}
#[test]
fn err_full_error_message() -> Result<()> {
let result = verify_that!(Err::<i32, i32>(1), err(eq(2)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: Err::<i32, i32>(1)
Expected: is an error which is equal to 2
Actual: Err(1),
which is an error
which isn't equal to 2
"
))))
)
}
#[test]
fn err_describe_match() -> Result<()> {
let matcher = err(eq(1));
verify_that!(
Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is an error which is equal to 1"))
)
}
#[test]
fn err_describe_no_match() -> Result<()> {
let matcher = err(eq(1));
verify_that!(
Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::NoMatch),
displays_as(eq(
"is a success or is an error containing a value which isn't equal to 1"
))
)
}
#[test]
fn err_describe_match_by_ref() -> Result<()> {
let matcher = err(eq("123"));
verify_that!(
Matcher::<&std::result::Result<String, String>>::describe(
&matcher,
MatcherResult::Match
),
displays_as(eq("is an error which is equal to \"123\""))
)
}
#[test]
fn err_describe_no_match_by_ref() -> Result<()> {
let matcher = err(eq("123"));
verify_that!(
Matcher::<&std::result::Result<String, String>>::describe(
&matcher,
MatcherResult::NoMatch
),
displays_as(eq(
"is a success or is an error containing a value which isn't equal to \"123\""
))
)
}
#[test]
fn err_explain_match_success() -> Result<()> {
let actual: std::result::Result<i32, i32> = Err(1);
let matcher = err(eq(1));
verify_that!(
matcher.explain_match(actual),
displays_as(eq("which is an error\n which is equal to 1"))
)
}
#[test]
fn err_explain_match_fail() -> Result<()> {
let actual: std::result::Result<i32, i32> = Err(2);
let matcher = err(eq(1));
verify_that!(
matcher.explain_match(actual),
displays_as(eq("which is an error\n which isn't equal to 1"))
)
}
#[test]
fn err_explain_match_ok() -> Result<()> {
let actual: std::result::Result<i32, i32> = Ok(1);
let matcher = err(eq(1));
verify_that!(matcher.explain_match(actual), displays_as(eq("which is a success")))
}
#[test]
fn err_explain_match_success_by_ref() -> Result<()> {
let actual: std::result::Result<String, String> = Err("123".to_string());
let matcher = err(eq("123"));
verify_that!(
matcher.explain_match(&actual),
displays_as(eq("which is an error\n which is equal to \"123\""))
)
}
#[test]
fn err_explain_match_fail_by_ref() -> Result<()> {
let actual: std::result::Result<String, String> = Err("321".to_string());
let matcher = err(eq("123"));
verify_that!(
matcher.explain_match(&actual),
displays_as(eq("which is an error\n which isn't equal to \"123\""))
)
}
#[test]
fn err_explain_match_ok_by_ref() -> Result<()> {
let actual: std::result::Result<String, String> = Ok("123".to_string());
let matcher = err(eq("123"));
verify_that!(matcher.explain_match(&actual), displays_as(eq("which is a success")))
}
}
@@ -0,0 +1,360 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module; the declarative
// macro is documented in the matchers module.
#![doc(hidden)]
/// Matches a structure or enum with a given field which is matched by a given
/// matcher.
///
/// This takes two arguments:
///
/// * a specification of the field against which to match, and
/// * an inner [`Matcher`][crate::matcher::Matcher] to apply to that field.
///
/// For example:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// struct IntField {
/// int: i32
/// }
/// # fn should_pass() -> Result<()> {
/// verify_that!(IntField{int: 32}, field!(&IntField.int, eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// Tuple structs are also supported via the index syntax:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// struct IntField(i32);
/// # fn should_pass() -> Result<()> {
/// verify_that!(IntField(32), field!(&IntField.0, eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// Enums are also supported, in which case only the specified variant is
/// matched:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// enum MyEnum {
/// A(i32),
/// B,
/// }
/// # fn should_pass() -> Result<()> {
/// verify_that!(MyEnum::A(32), field!(&MyEnum::A.0, eq(32)))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!(MyEnum::B, field!(&MyEnum::A.0, eq(32)))?; // Fails: wrong enum variant
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
/// The structure or enum may also be referenced from a separate module:
///
/// ```
/// # use googletest::prelude::*;
/// mod a_module {
/// #[derive(Debug)]
/// pub struct AStruct(pub i32);
/// }
/// # fn should_pass() -> Result<()> {
/// verify_that!(a_module::AStruct(32), field!(&a_module::AStruct.0, eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// If the inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// struct IntField {
/// int: i32
/// }
/// # fn should_pass() -> Result<()> {
/// verify_that!(IntField{int: 32}, field!(&IntField.int, 32))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// Nested structures are *not supported*, however:
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// struct InnerStruct(i32);
/// #[derive(Debug)]
/// struct OuterStruct {
/// inner: InnerStruct,
/// }
/// # fn should_not_compile() -> Result<()> {
/// verify_that!(value, field!(OuterStruct.inner.0, eq(32)))?; // Does not compile
/// # Ok(())
/// # }
/// ```
///
/// # Specification of the field pattern
///
/// The specification of the field follow the syntax: `(ref)? (&)?
/// $TYPE.$FIELD`.
/// The `&` allows to specify whether this matcher matches against an actual of
/// type `$TYPE` (`$TYPE`` must implement `Copy`) or a `&$TYPE`.
///
/// For instance:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// pub struct AStruct{a_field: i32};
/// # fn should_pass() -> Result<()> {
/// verify_that!(AStruct{a_field: 32}, field!(&AStruct.a_field, eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug, Clone, Copy)]
/// pub struct AStruct{a_field: i32};
/// # fn should_pass() -> Result<()> {
/// verify_that!(AStruct{a_field: 32}, field!(AStruct.a_field, eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// The `ref` allows to bind the field value by reference, which is required if
/// the field type does not implement `Copy`.
///
/// For instance:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// pub struct AStruct{a_field: i32};
/// # fn should_pass() -> Result<()> {
/// verify_that!(AStruct{a_field: 32}, field!(&AStruct.a_field, eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// If `field!` is qualified by both `&` and `ref`, they can both be omitted.
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// pub struct AStruct{a_field: String};
/// # fn should_pass() -> Result<()> {
/// verify_that!(AStruct{a_field: "32".into()}, field!(&AStruct.a_field, ref eq("32")))?;
/// verify_that!(AStruct{a_field: "32".into()}, field!(AStruct.a_field, eq("32")))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// See also the macro [`property`][crate::matchers::property] for an analogous
/// mechanism to extract a datum by invoking a method.
#[macro_export]
#[doc(hidden)]
macro_rules! __field {
($($t:tt)*) => { $crate::field_internal!($($t)*) }
}
// Internal-only macro created so that the macro definition does not appear in
// generated documentation.
// We cannot use `path` or `ty` to capture the type as we are terminating the
// type with a . (dot).
#[doc(hidden)]
#[macro_export]
macro_rules! field_internal {
// `ref` variant.
(
// The 3 cases of `$(& $($_amp:literal)?)?` -> `$(& $($_amp)*)*`
// 1. outer group captures nothing => expansion produces nothing
// 2. outer group captures just `&` => expansion produces `&`
// 3. outer group captures `& <literal>` => disallowed by `@assert_empty` subrule invocation
//
// `$_amp:literal` works only because the following `$t:ident` or `::` can't be captured by
// it.
$(& $($_amp:literal)?)? // Optional `&`'s presence is implicitly captured by `_amp`.
$(:: $($_cs:literal)?)? // Optional `::`'s presence is implicitly captured by `_cs`.
$($t:ident)::+ $(::<$($t_ty_args:ty),* $(,)?>)? .$field:tt,
ref $m:expr) => {{
$crate::field_internal!(@assert_empty $($($_amp)*)* $($($_cs)*)*);
$crate::field_internal!(@internal
struct_type: [&_]
field_prefix: [$(& $($_amp)*)* $(:: $($_cs)*)* $($t)::* $(::<$($t_ty_args),*>)*]
[$field] [ref] [$m])
}};
// Non-`ref` variant.
(
// See comment on previous variant above.
$(& $($_amp:literal)?)? // Optional `&`'s presence is implicitly captured by `_amp`.
$(:: $($_cs:literal)?)? // Optional `::`'s presence is implicitly captured by `_cs`.
$($t:ident)::+ $(::<$($t_ty_args:ty),* $(,)?>)? .$field:tt, $m:expr) => {{
$crate::field_internal!(@assert_empty $($($_amp)*)* $($($_cs)*)*);
$crate::field_internal!(@internal
struct_type: [$(& $($_amp)*)* &_]
field_prefix: [$(& $($_amp)*)* $(:: $($_cs)*)* $($t)::* $(::<$($t_ty_args),*>)*]
[$field] [] [$m])
}};
(@assert_empty) => {};
(@assert_empty $($l:literal)+) => {
compile_error!("property! argument must start with an optional `&` followed by a path")
};
(@internal struct_type: [$struct_ty:ty]
field_prefix: [$($field_prefix:tt)*]
[$field:tt] [$($ref:tt)?] [$m:expr]) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher(
|o: $struct_ty| {
match o {
$($field_prefix)* {$field: $($ref)* value, .. } => Some(value),
// The pattern below is unreachable if the type is a struct (as opposed to an
// enum). Since the macro can't know which it is, we always include it and just
// tell the compiler not to complain.
#[allow(unreachable_patterns)]
_ => None,
}
},
&stringify!($field),
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
}}
}
/// Functions for use only by the declarative macros in this module.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
pub mod internal {
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Creates a matcher to verify a specific field of the actual struct using
/// the provided inner matcher.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
pub fn field_matcher<OuterT, InnerT, InnerMatcher>(
field_accessor: fn(&OuterT) -> Option<&InnerT>,
field_path: &'static str,
inner: InnerMatcher,
) -> FieldMatcher<OuterT, InnerT, InnerMatcher> {
FieldMatcher { field_accessor, field_path, inner }
}
#[derive(MatcherBase)]
pub struct FieldMatcher<OuterT, InnerT, InnerMatcher> {
field_accessor: fn(&OuterT) -> Option<&InnerT>,
field_path: &'static str,
inner: InnerMatcher,
}
impl<'a, OuterT: Debug + 'a, InnerT: Debug + 'a, InnerMatcher: Matcher<&'a InnerT>>
Matcher<&'a OuterT> for FieldMatcher<OuterT, InnerT, InnerMatcher>
{
fn matches(&self, actual: &'a OuterT) -> MatcherResult {
if let Some(value) = (self.field_accessor)(actual) {
self.inner.matches(value)
} else {
MatcherResult::NoMatch
}
}
fn explain_match(&self, actual: &'a OuterT) -> Description {
if let Some(actual) = (self.field_accessor)(actual) {
format!(
"which has field `{}`, {}",
self.field_path,
self.inner.explain_match(actual)
)
.into()
} else {
let formatted_actual_value = format!("{actual:?}");
let without_fields = formatted_actual_value.split('(').next().unwrap_or("");
let without_fields = without_fields.split('{').next().unwrap_or("").trim_end();
format!("which has the wrong enum variant `{without_fields}`").into()
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"has field `{}`, which {}",
self.field_path,
self.inner.describe(matcher_result)
)
.into()
}
}
impl<OuterT: Debug + Copy, InnerT: Debug + Copy, InnerMatcher: Matcher<InnerT>> Matcher<OuterT>
for FieldMatcher<OuterT, InnerT, InnerMatcher>
{
fn matches(&self, actual: OuterT) -> MatcherResult {
if let Some(value) = (self.field_accessor)(&actual) {
self.inner.matches(*value)
} else {
MatcherResult::NoMatch
}
}
fn explain_match(&self, actual: OuterT) -> Description {
if let Some(actual) = (self.field_accessor)(&actual) {
format!(
"which has field `{}`, {}",
self.field_path,
self.inner.explain_match(*actual)
)
.into()
} else {
let formatted_actual_value = format!("{actual:?}");
let without_fields = formatted_actual_value.split('(').next().unwrap_or("");
let without_fields = without_fields.split('{').next().unwrap_or("").trim_end();
format!("which has the wrong enum variant `{without_fields}`").into()
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"has field `{}`, which {}",
self.field_path,
self.inner.describe(matcher_result)
)
.into()
}
}
}
@@ -0,0 +1,219 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a value greater than or equal to (in the sense of `>=`) `expected`.
///
/// The types of `ActualT` of `actual` and `ExpectedT` of `expected` must be
/// comparable via the `PartialOrd` trait. Namely, `ActualT` must implement
/// `PartialOrd<ExpectedT>`.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(1, ge(0))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!(0, ge(1))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
/// In most cases the params neeed to be the same type or they need to be cast
/// explicitly. This can be surprising when comparing integer types or
/// references:
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// # fn should_not_compile() -> Result<()> {
/// verify_that!(123u32, ge(0u64))?; // Does not compile
/// verify_that!(123u32 as u64, ge(0u64))?; // Passes
/// # Ok(())
/// # }
/// ```
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// # fn should_not_compile() -> Result<()> {
/// let actual: &u32 = &2;
/// let expected: u32 = 0;
/// verify_that!(actual, ge(expected))?; // Does not compile
/// # Ok(())
/// # }
/// ```
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let actual: &u32 = &2;
/// let expected: u32 = 0;
/// verify_that!(actual, ge(&expected))?; // Compiles and passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// You can find the standard library `PartialOrd` implementation in
/// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
pub fn ge<ExpectedT>(expected: ExpectedT) -> GeMatcher<ExpectedT> {
GeMatcher { expected }
}
#[derive(MatcherBase)]
pub struct GeMatcher<ExpectedT> {
expected: ExpectedT,
}
impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
for GeMatcher<ExpectedT>
{
fn matches(&self, actual: ActualT) -> MatcherResult {
(actual >= self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("is greater than or equal to {:?}", self.expected).into()
}
MatcherResult::NoMatch => format!("is less than {:?}", self.expected).into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::ffi::OsString;
#[test]
fn ge_matches_i32_with_i32() -> Result<()> {
let actual: i32 = 0;
let expected: i32 = 0;
verify_that!(actual, ge(expected))
}
#[test]
fn ge_does_not_match_smaller_i32() -> Result<()> {
let matcher = ge(10);
let result = matcher.matches(9);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn ge_matches_bigger_str() -> Result<()> {
verify_that!("B", ge("A"))
}
#[test]
fn ge_does_not_match_lesser_str() -> Result<()> {
let matcher = ge("z");
let result = matcher.matches("a");
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn ge_mismatch_contains_actual_and_expected() -> Result<()> {
let result = verify_that!(591, ge(927));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 591
Expected: is greater than or equal to 927
Actual: 591,
which is less than 927
"
))))
)
}
// Test `ge` matcher where actual is `&OsString` and expected is `&str`.
// Note that stdlib is a little bit inconsistent: `PartialOrd` exists for
// `OsString` and `str`, but only in one direction: it's only possible to
// compare `OsString` with `str` if `OsString` is on the left side of the
// ">=" operator (`impl PartialOrd<str> for OsString`).
//
// The comparison in the other direction is not defined.
//
// This means that the test case bellow effectively ensures that
// `verify_that(actual, ge(expected))` works if `actual >= expected` works
// (regardless whether the `expected >= actual` works`).
#[test]
fn ge_matches_owned_osstring_reference_with_string_reference() -> Result<()> {
let expected = "A";
let actual: OsString = "B".to_string().into();
verify_that!(&actual, ge(expected))
}
#[test]
fn ge_matches_ipv6addr_with_ipaddr() -> Result<()> {
use std::net::IpAddr;
use std::net::Ipv6Addr;
let actual: Ipv6Addr = "2001:4860:4860::8844".parse().unwrap();
let expected: IpAddr = "127.0.0.1".parse().unwrap();
verify_that!(actual, ge(expected))
}
#[test]
fn ge_matches_with_custom_partial_ord() -> Result<()> {
/// A custom "number" that is lower than all other numbers. The only
/// things we define about this "special" number is `PartialOrd` and
/// `PartialEq` against `u32`.
#[derive(Debug)]
struct VeryLowNumber {}
impl std::cmp::PartialEq<u32> for VeryLowNumber {
fn eq(&self, _other: &u32) -> bool {
false
}
}
// PartialOrd (required for >) requires PartialEq.
impl std::cmp::PartialOrd<u32> for VeryLowNumber {
fn partial_cmp(&self, _other: &u32) -> Option<std::cmp::Ordering> {
Some(std::cmp::Ordering::Less)
}
}
impl std::cmp::PartialEq<VeryLowNumber> for u32 {
fn eq(&self, _other: &VeryLowNumber) -> bool {
false
}
}
impl std::cmp::PartialOrd<VeryLowNumber> for u32 {
fn partial_cmp(&self, _other: &VeryLowNumber) -> Option<std::cmp::Ordering> {
Some(std::cmp::Ordering::Greater)
}
}
let actual: u32 = 42;
let expected = VeryLowNumber {};
verify_that!(actual, ge(expected))
}
}
@@ -0,0 +1,258 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a value greater (in the sense of `>`) than `expected`.
///
/// The types of `ActualT` of `actual` and `ExpectedT` of `expected` must be
/// comparable via the `PartialOrd` trait. Namely, `ActualT` must implement
/// `PartialOrd<ExpectedT>`.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(38, gt(1))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!(234, gt(234))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
/// In most cases the params neeed to be the same type or they need to be cast
/// explicitly. This can be surprising when comparing integer types or
/// references:
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// # fn should_not_compile() -> Result<()> {
/// verify_that!(123u32, gt(0u64))?; // Does not compile
/// verify_that!(123u32 as u64, gt(0u64))?; // Passes
/// # Ok(())
/// # }
/// ```
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// # fn should_not_compile() -> Result<()> {
/// let actual: &u32 = &2;
/// let expected: u32 = 1;
/// verify_that!(actual, gt(expected))?; // Does not compile
/// # Ok(())
/// # }
/// ```
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let actual: &u32 = &2;
/// let expected: u32 = 1;
/// verify_that!(actual, gt(&expected))?; // Compiles and passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// You can find the standard library `PartialOrd` implementation in
/// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
pub fn gt<ExpectedT: Debug>(expected: ExpectedT) -> GtMatcher<ExpectedT> {
GtMatcher { expected }
}
#[derive(MatcherBase)]
pub struct GtMatcher<ExpectedT> {
expected: ExpectedT,
}
impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
for GtMatcher<ExpectedT>
{
fn matches(&self, actual: ActualT) -> MatcherResult {
(actual > self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!("is greater than {:?}", self.expected).into(),
MatcherResult::NoMatch => {
format!("is less than or equal to {:?}", self.expected).into()
}
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::ffi::OsString;
#[test]
fn gt_matches_i32_with_i32() -> Result<()> {
let actual: i32 = 321;
let expected: i32 = 123;
verify_that!(actual, gt(expected))
}
#[test]
fn gt_does_not_match_equal_i32() -> Result<()> {
let matcher = gt(10);
let result = matcher.matches(10);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn gt_does_not_match_lower_i32() -> Result<()> {
let matcher = gt(-50);
let result = matcher.matches(-51);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn gt_matches_greater_str() -> Result<()> {
verify_that!("B", gt("A"))
}
#[test]
fn gt_does_not_match_lesser_str() -> Result<()> {
let matcher = gt("B");
let result = matcher.matches("A");
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn gt_mismatch_contains_actual_and_expected() -> Result<()> {
let result = verify_that!(481, gt(632));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 481
Expected: is greater than 632
Actual: 481,
"
))))
)
}
#[test]
fn gt_mismatch_combined_with_each() -> Result<()> {
let result = verify_that!(vec![19, 23, 11], each(gt(&15)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![19, 23, 11]
Expected: only contains elements that is greater than 15
Actual: [19, 23, 11],
whose element #2 is 11, which is less than or equal to 15
"
))))
)
}
#[test]
fn gt_describe_matches() -> Result<()> {
verify_that!(
Matcher::<i32>::describe(&gt(232), MatcherResult::Match),
displays_as(eq("is greater than 232"))
)
}
#[test]
fn gt_describe_does_not_match() -> Result<()> {
verify_that!(
Matcher::<i32>::describe(&gt(232), MatcherResult::NoMatch),
displays_as(eq("is less than or equal to 232"))
)
}
// Test `gt` matcher where actual is `&OsString` and expected is `&str`.
// Note that stdlib is a little bit inconsistent: `PartialOrd` exists for
// `OsString` and `str`, but only in one direction: it's only possible to
// compare `OsString` with `str` if `OsString` is on the left side of the
// ">" operator (`impl PartialOrd<str> for OsString`).
//
// The comparison in the other direction is not defined.
//
// This means that the test case bellow effectively ensures that
// `verify_that(actual, gt(expected))` works if `actual > expected` works
// (regardless whether the `expected > actual` works`).
#[test]
fn gt_matches_owned_osstring_reference_with_string_reference() -> Result<()> {
let expected = "A";
let actual: OsString = "B".to_string().into();
verify_that!(&actual, gt(expected))
}
#[test]
fn gt_matches_ipv6addr_with_ipaddr() -> Result<()> {
use std::net::IpAddr;
use std::net::Ipv6Addr;
let actual: Ipv6Addr = "2001:4860:4860::8888".parse().unwrap();
let expected: IpAddr = "127.0.0.1".parse().unwrap();
verify_that!(actual, gt(expected))
}
#[test]
fn gt_matches_with_custom_partial_ord() -> Result<()> {
/// A custom "number" that is smaller than all other numbers. The only
/// things we define about this "special" number is `PartialOrd` and
/// `PartialEq` against `u32`.
#[derive(Debug)]
struct VeryLowNumber {}
impl std::cmp::PartialEq<u32> for VeryLowNumber {
fn eq(&self, _other: &u32) -> bool {
false
}
}
// PartialOrd (required for >) requires PartialEq.
impl std::cmp::PartialOrd<u32> for VeryLowNumber {
fn partial_cmp(&self, _other: &u32) -> Option<std::cmp::Ordering> {
Some(std::cmp::Ordering::Less)
}
}
impl std::cmp::PartialEq<VeryLowNumber> for u32 {
fn eq(&self, _other: &VeryLowNumber) -> bool {
false
}
}
impl std::cmp::PartialOrd<VeryLowNumber> for u32 {
fn partial_cmp(&self, _other: &VeryLowNumber) -> Option<std::cmp::Ordering> {
Some(std::cmp::Ordering::Greater)
}
}
let actual: u32 = 42;
let expected = VeryLowNumber {};
verify_that!(actual, gt(expected))
}
}
@@ -0,0 +1,178 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
/// Matches a `&HashMap` containing the given `key` whose value is matched by
/// the matcher `inner`.
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashMap;
/// # fn should_pass() -> Result<()> {
/// let value = HashMap::from([(0, 1), (1, -1)]);
/// verify_that!(value, has_entry(0, eq(&1)))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
/// # let value = HashMap::from([(0, 1), (1, -1)]);
/// verify_that!(value, has_entry(1, gt(&0)))?; // Fails: value not matched
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// # let value = HashMap::from([(0, 1), (1, -1)]);
/// verify_that!(value, has_entry(2, eq(&0)))?; // Fails: key not present
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
///
/// Note: One could obtain the same effect by using `contains` and a
/// `Matcher<(&Key, &Value)>`:
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashMap;
/// # fn should_pass() -> Result<()> {
/// let value = HashMap::from([(0, 1), (1, -1)]);
/// verify_that!(value, contains(eq((&0, &1))))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
pub fn has_entry<KeyT, MatcherT>(key: KeyT, inner: MatcherT) -> HasEntryMatcher<KeyT, MatcherT> {
HasEntryMatcher { key, inner }
}
#[derive(MatcherBase)]
pub struct HasEntryMatcher<KeyT, MatcherT> {
key: KeyT,
inner: MatcherT,
}
impl<'a, KeyT: Debug + Eq + Hash, ValueT: Debug, MatcherT: Matcher<&'a ValueT>>
Matcher<&'a HashMap<KeyT, ValueT>> for HasEntryMatcher<KeyT, MatcherT>
{
fn matches(&self, actual: &'a HashMap<KeyT, ValueT>) -> MatcherResult {
if let Some(value) = actual.get(&self.key) {
self.inner.matches(value)
} else {
MatcherResult::NoMatch
}
}
fn explain_match(&self, actual: &'a HashMap<KeyT, ValueT>) -> Description {
if let Some(value) = actual.get(&self.key) {
format!(
"which contains key {:?}, but is mapped to value {:#?}, {}",
self.key,
value,
self.inner.explain_match(value)
)
.into()
} else {
format!("which doesn't contain key {:?}", self.key).into()
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!(
"contains key {:?}, which value {}",
self.key,
self.inner.describe(MatcherResult::Match)
)
.into(),
MatcherResult::NoMatch => format!(
"doesn't contain key {:?} or contains key {:?}, which value {}",
self.key,
self.key,
self.inner.describe(MatcherResult::NoMatch)
)
.into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::collections::HashMap;
#[test]
fn has_entry_does_not_match_empty_hash_map() -> Result<()> {
let value: HashMap<i32, i32> = HashMap::new();
verify_that!(&value, not(has_entry(0, eq(&0))))
}
#[test]
fn has_entry_matches_hash_map_with_value() -> Result<()> {
let value: HashMap<i32, i32> = HashMap::from([(0, 0)]);
verify_that!(&value, has_entry(0, eq(&0)))
}
#[test]
fn has_entry_does_not_match_hash_map_with_wrong_value() -> Result<()> {
let value: HashMap<i32, i32> = HashMap::from([(0, 1)]);
verify_that!(&value, not(has_entry(0, eq(&0))))
}
#[test]
fn has_entry_does_not_match_hash_map_with_wrong_key() -> Result<()> {
let value: HashMap<i32, i32> = HashMap::from([(1, 0)]);
verify_that!(&value, not(has_entry(0, eq(&0))))
}
#[test]
fn has_entry_shows_correct_message_when_key_is_not_present() -> Result<()> {
let result = verify_that!(HashMap::from([(0, 0)]), has_entry(1, eq(&0)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: HashMap::from([(0, 0)])
Expected: contains key 1, which value is equal to 0
Actual: {0: 0},
which doesn't contain key 1
"
))))
)
}
#[test]
fn has_entry_shows_correct_message_when_key_has_non_matching_value() -> Result<()> {
let result = verify_that!(HashMap::from([(0, 0)]), has_entry(0, eq(&1)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: HashMap::from([(0, 0)])
Expected: contains key 0, which value is equal to 1
Actual: {0: 0},
which contains key 0, but is mapped to value 0, which isn't equal to 1
"
))))
)
}
}
@@ -0,0 +1,176 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a byte sequence which is a UTF-8 encoded string matched by `inner`.
///
/// The matcher reports no match if either the string is not UTF-8 encoded or if
/// `inner` does not match on the decoded string.
///
/// The input may be a slice `&[u8]` or a `Vec` of bytes.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let bytes: &[u8] = "A string".as_bytes();
/// verify_that!(bytes, is_utf8_string(eq("A string")))?; // Passes
/// let bytes: Vec<u8> = "A string".as_bytes().to_vec();
/// verify_that!(bytes, is_utf8_string(eq("A string")))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
/// # let bytes: &[u8] = "A string".as_bytes();
/// verify_that!(bytes, is_utf8_string(eq("Another string")))?; // Fails (inner matcher does not match)
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// let bytes: Vec<u8> = vec![255, 64, 128, 32];
/// verify_that!(bytes, is_utf8_string(anything()))?; // Fails (not UTF-8 encoded)
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
pub fn is_utf8_string<InnerMatcherT>(inner: InnerMatcherT) -> IsEncodedStringMatcher<InnerMatcherT>
where
InnerMatcherT: for<'a> Matcher<&'a str>,
{
IsEncodedStringMatcher { inner }
}
#[derive(MatcherBase)]
pub struct IsEncodedStringMatcher<InnerMatcherT> {
inner: InnerMatcherT,
}
impl<ActualT: AsRef<[u8]> + Debug + Copy, InnerMatcherT> Matcher<ActualT>
for IsEncodedStringMatcher<InnerMatcherT>
where
InnerMatcherT: for<'a> Matcher<&'a str>,
{
fn matches(&self, actual: ActualT) -> MatcherResult {
std::str::from_utf8(actual.as_ref())
.map(|s| self.inner.matches(s))
.unwrap_or(MatcherResult::NoMatch)
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!(
"is a UTF-8 encoded string which {}",
self.inner.describe(MatcherResult::Match)
)
.into(),
MatcherResult::NoMatch => format!(
"is not a UTF-8 encoded string which {}",
self.inner.describe(MatcherResult::Match)
)
.into(),
}
}
fn explain_match(&self, actual: ActualT) -> Description {
match std::str::from_utf8(actual.as_ref()) {
Ok(s) => {
format!("which is a UTF-8 encoded string {}", self.inner.explain_match(s)).into()
}
Err(e) => format!("which is not a UTF-8 encoded string: {e}").into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
#[test]
fn matches_string_as_byte_slice() -> Result<()> {
verify_that!("A string".as_bytes(), is_utf8_string(eq("A string")))
}
#[test]
fn matches_string_as_byte_vec() -> Result<()> {
verify_that!("A string".as_bytes().to_vec(), is_utf8_string(eq("A string")))
}
#[test]
fn matches_string_with_utf_8_encoded_sequences() -> Result<()> {
verify_that!("äöüÄÖÜ".as_bytes().to_vec(), is_utf8_string(eq("äöüÄÖÜ")))
}
#[test]
fn does_not_match_non_equal_string() -> Result<()> {
verify_that!("äöüÄÖÜ".as_bytes().to_vec(), not(is_utf8_string(eq("A string"))))
}
#[test]
fn does_not_match_non_utf_8_encoded_byte_sequence() -> Result<()> {
verify_that!(&[192, 64, 255, 32], not(is_utf8_string(eq("A string"))))
}
#[test]
fn has_correct_description_in_matched_case() -> Result<()> {
let matcher = is_utf8_string(eq("A string"));
verify_that!(
Matcher::<&[u8]>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is a UTF-8 encoded string which is equal to \"A string\""))
)
}
#[test]
fn has_correct_description_in_not_matched_case() -> Result<()> {
let matcher = is_utf8_string(eq("A string"));
verify_that!(
Matcher::<&[u8]>::describe(&matcher, MatcherResult::NoMatch),
displays_as(eq("is not a UTF-8 encoded string which is equal to \"A string\""))
)
}
#[test]
fn has_correct_explanation_in_matched_case() -> Result<()> {
let explanation = is_utf8_string(eq("A string")).explain_match("A string".as_bytes());
verify_that!(
explanation,
displays_as(eq("which is a UTF-8 encoded string which is equal to \"A string\""))
)
}
#[test]
fn has_correct_explanation_when_byte_array_is_not_utf8_encoded() -> Result<()> {
let explanation = is_utf8_string(eq("A string")).explain_match([192, 128, 0, 64]);
verify_that!(explanation, displays_as(starts_with("which is not a UTF-8 encoded string: ")))
}
#[test]
fn has_correct_explanation_when_inner_matcher_does_not_match() -> Result<()> {
let explanation = is_utf8_string(eq("A string")).explain_match("Another string".as_bytes());
verify_that!(
explanation,
displays_as(eq("which is a UTF-8 encoded string which isn't equal to \"A string\""))
)
}
}
@@ -0,0 +1,84 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use num_traits::float::Float;
use std::fmt::Debug;
/// Matches a floating point value which is Finite.
pub fn is_finite() -> IsFiniteMatcher {
IsFiniteMatcher
}
#[derive(MatcherBase)]
pub struct IsFiniteMatcher;
impl<T: Float + Debug + Copy> Matcher<T> for IsFiniteMatcher {
fn matches(&self, actual: T) -> MatcherResult {
actual.is_finite().into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
if matcher_result.into() { "is Finite" } else { "isn't Finite" }.into()
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
#[test]
fn matches_f32_number() -> Result<()> {
verify_that!(0.0f32, is_finite())
}
#[test]
fn does_not_match_f32_pos_infinity() -> Result<()> {
verify_that!(f32::INFINITY, not(is_finite()))
}
#[test]
fn does_not_match_f32_neg_infinity() -> Result<()> {
verify_that!(f32::NEG_INFINITY, not(is_finite()))
}
#[test]
fn does_not_match_f32_nan() -> Result<()> {
verify_that!(f32::NAN, not(is_finite()))
}
#[test]
fn matches_f64_number() -> Result<()> {
verify_that!(0.0f64, is_finite())
}
#[test]
fn does_not_match_f64_pos_infinity() -> Result<()> {
verify_that!(f64::INFINITY, not(is_finite()))
}
#[test]
fn does_not_match_f64_neg_infinity() -> Result<()> {
verify_that!(f64::NEG_INFINITY, not(is_finite()))
}
#[test]
fn does_not_match_f64_nan() -> Result<()> {
verify_that!(f64::NAN, not(is_finite()))
}
}
@@ -0,0 +1,84 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use num_traits::float::Float;
use std::fmt::Debug;
/// Matches a floating point value which is Infinite.
pub fn is_infinite() -> IsInfiniteMatcher {
IsInfiniteMatcher
}
#[derive(MatcherBase)]
pub struct IsInfiniteMatcher;
impl<T: Float + Debug + Copy> Matcher<T> for IsInfiniteMatcher {
fn matches(&self, actual: T) -> MatcherResult {
actual.is_infinite().into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
if matcher_result.into() { "is Infinite" } else { "isn't Infinite" }.into()
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
#[test]
fn matches_f32_pos_infinity() -> Result<()> {
verify_that!(f32::INFINITY, is_infinite())
}
#[test]
fn matches_f32_neg_infinity() -> Result<()> {
verify_that!(f32::NEG_INFINITY, is_infinite())
}
#[test]
fn does_not_match_f32_number() -> Result<()> {
verify_that!(0.0f32, not(is_infinite()))
}
#[test]
fn does_not_match_f32_nan() -> Result<()> {
verify_that!(f32::NAN, not(is_finite()))
}
#[test]
fn matches_f64_pos_infinity() -> Result<()> {
verify_that!(f64::INFINITY, is_infinite())
}
#[test]
fn matches_f64_neg_infinity() -> Result<()> {
verify_that!(f64::NEG_INFINITY, is_infinite())
}
#[test]
fn does_not_match_f64_number() -> Result<()> {
verify_that!(0.0f64, not(is_infinite()))
}
#[test]
fn does_not_match_f64_nan() -> Result<()> {
verify_that!(f64::NAN, not(is_finite()))
}
}
@@ -0,0 +1,65 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![doc(hidden)]
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches precisely values matched by `inner`.
///
/// The returned matcher produces a description prefixed by the string
/// `description`. This is useful in contexts where the test assertion failure
/// output must include the additional description.
pub fn is<InnerMatcherT>(description: &str, inner: InnerMatcherT) -> IsMatcher<'_, InnerMatcherT> {
IsMatcher { description, inner }
}
#[derive(MatcherBase)]
pub struct IsMatcher<'a, InnerMatcherT> {
description: &'a str,
inner: InnerMatcherT,
}
impl<ActualT: Debug + Copy, InnerMatcherT: Matcher<ActualT>> Matcher<ActualT>
for IsMatcher<'_, InnerMatcherT>
{
fn matches(&self, actual: ActualT) -> MatcherResult {
self.inner.matches(actual)
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!(
"is {} which {}",
self.description,
self.inner.describe(MatcherResult::Match)
)
.into(),
MatcherResult::NoMatch => format!(
"is not {} which {}",
self.description,
self.inner.describe(MatcherResult::Match)
)
.into(),
}
}
fn explain_match(&self, actual: ActualT) -> Description {
self.inner.explain_match(actual)
}
}
@@ -0,0 +1,64 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use num_traits::float::Float;
use std::fmt::Debug;
/// Matches a floating point value which is NaN.
pub fn is_nan() -> IsNanMatcher {
IsNanMatcher
}
#[derive(MatcherBase)]
pub struct IsNanMatcher;
impl<T: Float + Debug + Copy> Matcher<T> for IsNanMatcher {
fn matches(&self, actual: T) -> MatcherResult {
actual.is_nan().into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
if matcher_result.into() { "is NaN" } else { "isn't NaN" }.into()
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
#[test]
fn matches_f32_nan() -> Result<()> {
verify_that!(f32::NAN, is_nan())
}
#[test]
fn does_not_match_f32_number() -> Result<()> {
verify_that!(0.0f32, not(is_nan()))
}
#[test]
fn matches_f64_nan() -> Result<()> {
verify_that!(f64::NAN, is_nan())
}
#[test]
fn does_not_match_f64_number() -> Result<()> {
verify_that!(0.0f64, not(is_nan()))
}
}
@@ -0,0 +1,218 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a value less than or equal to (in the sense of `<=`) `expected`.
///
/// The types of `ActualT` of `actual` and `ExpectedT` of `expected` must be
/// comparable via the `PartialOrd` trait. Namely, `ActualT` must implement
/// `PartialOrd<ExpectedT>`.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(0, le(0))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!(1, le(0))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
/// In most cases the params neeed to be the same type or they need to be cast
/// explicitly. This can be surprising when comparing integer types or
/// references:
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// # fn should_not_compile() -> Result<()> {
/// verify_that!(1u32, le(2u64))?; // Does not compile
/// verify_that!(1u32 as u64, le(2u64))?; // Passes
/// # Ok(())
/// # }
/// ```
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// # fn should_not_compile() -> Result<()> {
/// let actual: &u32 = &1;
/// let expected: u32 = 2;
/// verify_that!(actual, le(expected))?; // Does not compile
/// # Ok(())
/// # }
/// ```
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let actual: &u32 = &1;
/// let expected: u32 = 2;
/// verify_that!(actual, le(&expected))?; // Compiles and passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// You can find the standard library `PartialOrd` implementation in
/// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
pub fn le<ExpectedT>(expected: ExpectedT) -> LeMatcher<ExpectedT> {
LeMatcher { expected }
}
#[derive(MatcherBase)]
pub struct LeMatcher<ExpectedT> {
expected: ExpectedT,
}
impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
for LeMatcher<ExpectedT>
{
fn matches(&self, actual: ActualT) -> MatcherResult {
(actual <= self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!("is less than or equal to {:?}", self.expected).into(),
MatcherResult::NoMatch => format!("is greater than {:?}", self.expected).into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::ffi::OsString;
#[test]
fn le_matches_i32_with_i32() -> Result<()> {
let actual: i32 = 0;
let expected: i32 = 0;
verify_that!(actual, le(expected))
}
#[test]
fn le_does_not_match_bigger_i32() -> Result<()> {
let matcher = le(0);
let result = matcher.matches(1);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn le_matches_smaller_str() -> Result<()> {
verify_that!("A", le("B"))
}
#[test]
fn le_does_not_match_bigger_str() -> Result<()> {
let matcher = le("a");
let result = matcher.matches("z");
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn le_mismatch_contains_actual_and_expected() -> Result<()> {
let result = verify_that!(489, le(294));
let formatted_message = format!("{}", result.unwrap_err());
verify_that!(
formatted_message.as_str(),
contains_substring(indoc!(
"
Value of: 489
Expected: is less than or equal to 294
Actual: 489,
which is greater than 294
"
))
)
}
// Test `le` matcher where actual is `&OsString` and expected is `&str`.
// Note that stdlib is a little bit inconsistent: `PartialOrd` exists for
// `OsString` and `str`, but only in one direction: it's only possible to
// compare `OsString` with `str` if `OsString` is on the left side of the
// "<=" operator (`impl PartialOrd<str> for OsString`).
//
// The comparison in the other direction is not defined.
//
// This means that the test case bellow effectively ensures that
// `verify_that(actual, le(expected))` works if `actual <= expected` works
// (regardless whether the `expected <= actual` works`).
#[test]
fn le_matches_owned_osstring_reference_with_string_reference() -> Result<()> {
let expected = "B";
let actual: OsString = "A".into();
verify_that!(&actual, le(expected))
}
#[test]
fn le_matches_ipv6addr_with_ipaddr() -> Result<()> {
use std::net::IpAddr;
use std::net::Ipv6Addr;
let actual: IpAddr = "127.0.0.1".parse().unwrap();
let expected: Ipv6Addr = "2001:4860:4860::8844".parse().unwrap();
verify_that!(actual, le(expected))
}
#[test]
fn le_matches_with_custom_partial_ord() -> Result<()> {
/// A custom "number" that is lower than all other numbers. The only
/// things we define about this "special" number is `PartialOrd` and
/// `PartialEq` against `u32`.
#[derive(Debug, Clone, Copy)]
struct VeryLowNumber {}
impl std::cmp::PartialEq<u32> for VeryLowNumber {
fn eq(&self, _other: &u32) -> bool {
false
}
}
// PartialOrd (required for >) requires PartialEq.
impl std::cmp::PartialOrd<u32> for VeryLowNumber {
fn partial_cmp(&self, _other: &u32) -> Option<std::cmp::Ordering> {
Some(std::cmp::Ordering::Less)
}
}
impl std::cmp::PartialEq<VeryLowNumber> for u32 {
fn eq(&self, _other: &VeryLowNumber) -> bool {
false
}
}
impl std::cmp::PartialOrd<VeryLowNumber> for u32 {
fn partial_cmp(&self, _other: &VeryLowNumber) -> Option<std::cmp::Ordering> {
Some(std::cmp::Ordering::Greater)
}
}
let actual = VeryLowNumber {};
let expected: u32 = 42;
verify_that!(actual, le(expected))
}
}
@@ -0,0 +1,209 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::count_elements::count_elements;
use std::fmt::Debug;
/// Matches a container whose number of elements matches `expected`.
///
/// This matches against a container over which one can iterate. This includes
/// the standard Rust containers, arrays, and slices. More
/// precisely, the actual type must implement [`IntoIterator`].
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let array = [1,2,3];
/// verify_that!(array, len(eq(3)))?;
/// let vec = vec![1,2,3];
/// verify_that!(vec, len(eq(3)))?;
/// let slice = vec.as_slice();
/// verify_that!(*slice, len(eq(3)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// The parameter `expected` can be any integer numeric matcher.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let vec = vec![1,2,3];
/// verify_that!(vec, len(gt(1)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
pub fn len<E>(expected: E) -> LenMatcher<E> {
LenMatcher { expected }
}
#[derive(MatcherBase)]
pub struct LenMatcher<E> {
expected: E,
}
impl<T: Debug + Copy, E: Matcher<usize>> Matcher<T> for LenMatcher<E>
where
T: IntoIterator,
{
fn matches(&self, actual: T) -> MatcherResult {
self.expected.matches(count_elements(actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("has length, which {}", self.expected.describe(MatcherResult::Match)).into()
}
MatcherResult::NoMatch => {
format!("has length, which {}", self.expected.describe(MatcherResult::NoMatch))
.into()
}
}
}
fn explain_match(&self, actual: T) -> Description {
let actual_size = count_elements(actual);
format!("which has length {}, {}", actual_size, self.expected.explain_match(actual_size))
.into()
}
}
#[cfg(test)]
mod tests {
use crate::description::Description;
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::collections::{
BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque,
};
use std::fmt::Debug;
#[test]
fn len_matcher_match_vec() -> Result<()> {
let value = vec![1, 2, 3];
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_array_reference() -> Result<()> {
let value = &[1, 2, 3];
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_slice_of_array() -> Result<()> {
let value = &[1, 2, 3];
verify_that!(value[0..1], len(eq(1)))
}
#[test]
fn len_matcher_match_slice_of_vec() -> Result<()> {
let value = vec![1, 2, 3];
let slice = value.as_slice();
verify_that!(slice, len(eq(3)))
}
#[test]
fn len_matcher_match_sized_slice() -> Result<()> {
let value = [1, 2, 3];
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_btreemap() -> Result<()> {
let value = BTreeMap::from([(1, 2), (2, 3), (3, 4)]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_btreeset() -> Result<()> {
let value = BTreeSet::from([1, 2, 3]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_binaryheap() -> Result<()> {
let value = BinaryHeap::from([1, 2, 3]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_hashmap() -> Result<()> {
let value = HashMap::from([(1, 2), (2, 3), (3, 4)]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_hashset() -> Result<()> {
let value = HashSet::from([1, 2, 3]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_linkedlist() -> Result<()> {
let value = LinkedList::from([1, 2, 3]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_match_vecdeque() -> Result<()> {
let value = VecDeque::from([1, 2, 3]);
verify_that!(value, len(eq(3)))
}
#[test]
fn len_matcher_explain_match() -> Result<()> {
#[derive(MatcherBase)]
struct TestMatcher;
impl<T: Debug + Copy> Matcher<T> for TestMatcher {
fn matches(&self, _: T) -> MatcherResult {
false.into()
}
fn describe(&self, _: MatcherResult) -> Description {
"called described".into()
}
fn explain_match(&self, _: T) -> Description {
"called explain_match".into()
}
}
verify_that!(
len(TestMatcher).explain_match([1, 2, 3]),
displays_as(eq("which has length 3, called explain_match"))
)
}
#[test]
fn len_matcher_error_message() -> Result<()> {
let result = verify_that!(vec![1, 2, 3, 4], len(eq(3)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![1, 2, 3, 4]
Expected: has length, which is equal to 3
Actual: [1, 2, 3, 4],
which has length 4, which isn't equal to 3"
))))
)
}
}
@@ -0,0 +1,227 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a value less (in the sense of `<`) than `expected`.
///
/// The types of `ActualT` of `actual` and `ExpectedT` of `expected` must be
/// comparable via the `PartialOrd` trait. Namely, `ActualT` must implement
/// `PartialOrd<ExpectedT>`.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(1, lt(2))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!(2, lt(2))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
/// In most cases the params neeed to be the same type or they need to be cast
/// explicitly. This can be surprising when comparing integer types or
/// references:
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// # fn should_not_compile() -> Result<()> {
/// verify_that!(123u32, lt(0u64))?; // Does not compile
/// verify_that!(123u32 as u64, lt(100000000u64))?; // Passes
/// # Ok(())
/// # }
/// ```
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// # fn should_not_compile() -> Result<()> {
/// let actual: &u32 = &2;
/// let expected: u32 = 70;
/// verify_that!(actual, lt(expected))?; // Does not compile
/// # Ok(())
/// # }
/// ```
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let actual: &u32 = &2;
/// let expected: u32 = 70;
/// verify_that!(actual, lt(&expected))?; // Compiles and passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// You can find the standard library `PartialOrd` implementation in
/// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
pub fn lt<ExpectedT>(expected: ExpectedT) -> LtMatcher<ExpectedT> {
LtMatcher { expected }
}
#[derive(MatcherBase)]
pub struct LtMatcher<ExpectedT> {
expected: ExpectedT,
}
impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
for LtMatcher<ExpectedT>
{
fn matches(&self, actual: ActualT) -> MatcherResult {
(actual < self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!("is less than {:?}", self.expected).into(),
MatcherResult::NoMatch => {
format!("is greater than or equal to {:?}", self.expected).into()
}
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::ffi::OsString;
#[test]
fn lt_matches_i32_with_i32() -> Result<()> {
let actual: i32 = 10000;
let expected: i32 = 20000;
verify_that!(actual, lt(expected))
}
#[test]
fn lt_does_not_match_equal_i32() -> Result<()> {
let matcher = lt(10);
let result = matcher.matches(10);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn lt_does_not_match_lower_i32() -> Result<()> {
let matcher = lt(-50);
let result = matcher.matches(50);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn lt_matches_lesser_str() -> Result<()> {
verify_that!("A", lt("B"))
}
#[test]
fn lt_does_not_match_bigger_str() -> Result<()> {
let matcher = lt("ab");
let result = matcher.matches("az");
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn lt_mismatch_contains_actual_and_expected() -> Result<()> {
let result = verify_that!(481, lt(45));
let formatted_message = format!("{}", result.unwrap_err());
verify_that!(
formatted_message.as_str(),
contains_substring(indoc!(
"
Value of: 481
Expected: is less than 45
Actual: 481,
which is greater than or equal to 45
"
))
)
}
// Test `lt` matcher where actual is `&OsString` and expected is `&str`.
// Note that stdlib is a little bit inconsistent: `PartialOrd` exists for
// `OsString` and `str`, but only in one direction: it's only possible to
// compare `OsString` with `str` if `OsString` is on the left side of the
// "<" operator (`impl PartialOrd<str> for OsString`).
//
// The comparison in the other direction is not defined.
//
// This means that the test case bellow effectively ensures that
// `verify_that(actual, lt(expected))` works if `actual < expected` works
// (regardless whether the `expected < actual` works`).
#[test]
fn lt_matches_owned_osstring_reference_with_string_reference() -> Result<()> {
let expected = "C";
let actual: OsString = "B".to_string().into();
verify_that!(&actual, lt(expected))
}
#[test]
fn lt_matches_ipv6addr_with_ipaddr() -> Result<()> {
use std::net::IpAddr;
use std::net::Ipv6Addr;
let actual: IpAddr = "127.0.0.1".parse().unwrap();
let expected: Ipv6Addr = "2001:4860:4860::8844".parse().unwrap();
verify_that!(actual, lt(expected))
}
#[test]
fn lt_matches_with_custom_partial_ord() -> Result<()> {
/// A custom "number" that is smaller than all other numbers. The only
/// things we define about this "special" number is `PartialOrd` and
/// `PartialEq` against `u32`.
#[derive(Debug, Clone, Copy)]
struct VeryLowNumber {}
impl std::cmp::PartialEq<u32> for VeryLowNumber {
fn eq(&self, _other: &u32) -> bool {
false
}
}
// PartialOrd (required for >) requires PartialEq.
impl std::cmp::PartialOrd<u32> for VeryLowNumber {
fn partial_cmp(&self, _other: &u32) -> Option<std::cmp::Ordering> {
Some(std::cmp::Ordering::Less)
}
}
impl std::cmp::PartialEq<VeryLowNumber> for u32 {
fn eq(&self, _other: &VeryLowNumber) -> bool {
false
}
}
impl std::cmp::PartialOrd<VeryLowNumber> for u32 {
fn partial_cmp(&self, _other: &VeryLowNumber) -> Option<std::cmp::Ordering> {
Some(std::cmp::Ordering::Greater)
}
}
let actual = VeryLowNumber {};
let expected: u32 = 42;
verify_that!(actual, lt(expected))
}
}
@@ -0,0 +1,523 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module; the declarative
// macro is documented in the matchers module.
#![doc(hidden)]
/// Matches a value according to a pattern of matchers.
///
/// This takes as an argument a specification similar to a struct or enum
/// initialiser, where each value is a [`Matcher`][crate::matcher::Matcher]
/// which is applied to the corresponding field.
///
/// This can be used to match arbitrary combinations of fields on structures
/// using arbitrary matchers:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// struct MyStruct {
/// a_field: String,
/// another_field: String,
/// }
///
/// let my_struct = MyStruct {
/// a_field: "Something to believe in".into(),
/// another_field: "Something else".into()
/// };
/// verify_that!(my_struct, matches_pattern!(MyStruct {
/// a_field: starts_with("Something"),
/// another_field: ends_with("else"),
/// }))
/// # .unwrap();
/// ```
///
/// If any fields are provided in the pattern, then all fields must be
/// specified, or the pattern must end with `..`, just like regular match
/// patterns. Omitted fields have no effect on the output of the matcher.
/// The `..` is unnecessary when no fields are provided and only method
/// values are checked.
///
/// ```
/// # use googletest::prelude::*;
/// # #[derive(Debug)]
/// # struct MyStruct {
/// # a_field: String,
/// # another_field: String,
/// # }
/// #
/// # let my_struct = MyStruct {
/// # a_field: "Something to believe in".into(),
/// # another_field: "Something else".into()
/// # };
/// verify_that!(my_struct, matches_pattern!(MyStruct {
/// a_field: starts_with("Something"),
/// .. // another_field is missing, so it may be anything.
/// }))
/// # .unwrap();
/// ```
///
/// One can use it recursively to match nested structures:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// struct MyStruct {
/// a_nested_struct: MyInnerStruct,
/// }
///
/// #[derive(Debug)]
/// struct MyInnerStruct {
/// a_field: String,
/// }
///
/// let my_struct = MyStruct {
/// a_nested_struct: MyInnerStruct { a_field: "Something to believe in".into() },
/// };
/// verify_that!(my_struct, matches_pattern!(MyStruct {
/// a_nested_struct: matches_pattern!(MyInnerStruct {
/// a_field: starts_with("Something"),
/// }),
/// }))
/// # .unwrap();
/// ```
///
/// One can use the alias [`pat`][crate::matchers::pat] to make this less
/// verbose:
///
/// ```
/// # use googletest::prelude::*;
/// # #[derive(Debug)]
/// # struct MyStruct {
/// # a_nested_struct: MyInnerStruct,
/// # }
/// #
/// # #[derive(Debug)]
/// # struct MyInnerStruct {
/// # a_field: String,
/// # }
/// #
/// # let my_struct = MyStruct {
/// # a_nested_struct: MyInnerStruct { a_field: "Something to believe in".into() },
/// # };
/// verify_that!(my_struct, matches_pattern!(MyStruct {
/// a_nested_struct: pat!(MyInnerStruct {
/// a_field: starts_with("Something"),
/// }),
/// }))
/// # .unwrap();
/// ```
///
/// In addition to fields, one can match on the outputs of methods
/// ("properties"):
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// struct MyStruct {
/// a_field: String,
/// }
///
/// impl MyStruct {
/// fn get_a_field(&self) -> String { self.a_field.clone() }
/// }
///
/// let my_struct = MyStruct { a_field: "Something to believe in".into() };
/// verify_that!(my_struct, matches_pattern!(MyStruct {
/// get_a_field(): starts_with("Something"),
/// }))
/// # .unwrap();
/// ```
///
/// If an inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// struct MyStruct {
/// a_field: String,
/// another_field: String,
/// }
///
/// let my_struct = MyStruct {
/// a_field: "this".into(),
/// another_field: "that".into()
/// };
/// verify_that!(my_struct, matches_pattern!(MyStruct {
/// a_field: "this",
/// another_field: "that",
/// }))
/// # .unwrap();
/// ```
///
/// **Important**: The method should be pure function with a deterministic
/// output and no side effects. In particular, in the event of an assertion
/// failure, it will be invoked a second time, with the assertion failure output
/// reflecting the *second* invocation.
///
/// These may also include extra litteral parameters you pass in:
///
/// ```
/// # use googletest::prelude::*;
/// # #[derive(Debug)]
/// # struct MyStruct {
/// # a_field: String,
/// # }
/// #
/// impl MyStruct {
/// fn append_to_a_field(&self, suffix: &str) -> String { self.a_field.clone() + suffix }
/// }
///
/// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
/// verify_that!(my_struct, matches_pattern!(&MyStruct {
/// append_to_a_field("a suffix"): ref ends_with("a suffix"),
/// }))
/// # .unwrap();
/// ```
///
/// You can precede both field and property matchers with a `ref` to match the
/// result by reference:
///
/// ```
/// # use googletest::prelude::*;
/// # #[derive(Debug)]
/// # struct MyStruct {
/// # a_field: String,
/// # }
/// #
/// impl MyStruct {
/// fn get_a_field_ref(&self) -> String { self.a_field.clone() }
/// }
///
/// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
/// verify_that!(my_struct, matches_pattern!(&MyStruct {
/// get_a_field_ref(): ref starts_with("Something"),
/// }))
/// # .unwrap();
/// ```
///
/// Note that if the `actual` is of type `&ActualT` and the pattern type is
/// `ActualT`, this is automatically performed. This behavior is similar to the
/// reference binding mode in pattern matching.
///
/// ```
/// # use googletest::prelude::*;
/// # #[derive(Debug)]
/// # struct MyStruct {
/// # a_field: String,
/// # }
/// #
/// impl MyStruct {
/// fn get_a_field_ref(&self) -> String { self.a_field.clone() }
/// }
///
/// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
/// verify_that!(my_struct, matches_pattern!(MyStruct {
/// get_a_field_ref(): starts_with("Something"),
/// }))
/// # .unwrap();
/// ```
///
/// One can also match tuple structs with up to 10 fields. In this case, all
/// fields must have matchers:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// struct MyTupleStruct(String, String);
///
/// let my_struct = MyTupleStruct("Something".into(), "Some other thing".into());
/// verify_that!(
/// my_struct,
/// matches_pattern!(&MyTupleStruct(ref eq("Something"), ref eq("Some other thing")))
/// )
/// # .unwrap();
/// ```
///
/// The macro also allows matching on specific enum values and supports wildcard
/// patterns like `MyEnum::Case(_)`.
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// enum MyEnum {
/// A(u32),
/// B,
/// }
///
/// # fn should_pass() -> Result<()> {
/// verify_that!(MyEnum::A(123), matches_pattern!(&MyEnum::A(eq(123))))?; // Passes
/// # Ok(())
/// # }
/// # fn should_pass_with_wildcard() -> Result<()> {
/// verify_that!(MyEnum::A(123), matches_pattern!(MyEnum::A(_)))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!(MyEnum::B, matches_pattern!(&MyEnum::A(eq(123))))?; // Fails - wrong enum variant
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_pass_with_wildcard().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
/// This macro does not support plain (non-struct) tuples. But it should not be
/// necessary as tuple of matchers are matchers of tuple. In other words, if
/// `MatcherU: Matcher<U>` and `MatcherT: Matcher<T>`, then `(MatcherU,
/// MatcherT): Matcher<(U, T)>`.
///
/// Trailing commas are allowed (but not required) in both ordinary and tuple
/// structs.
///
/// Note that the default format (rustfmt) can format macros if the macro
/// argument is parseable Rust code. This is mostly true for this macro with two
/// exceptions:
/// * property matching
/// * `ref` keyword with named fields
///
/// An option for formatting large is to avoid these exceptions (by removing the
/// parenthesis of properties and the `ref` keywords), run `rustfmt` and add
/// them back.
#[macro_export]
#[doc(hidden)]
macro_rules! __matches_pattern {
($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) }
}
// Internal-only macro created so that the macro definition does not appear in
// generated documentation.
#[doc(hidden)]
#[macro_export]
macro_rules! matches_pattern_internal {
($($tt:tt)*) => {
{
use $crate::{self as googletest};
#[allow(unused)]
use $crate::matchers::{all, field, property};
$crate::matchers::__internal_unstable_do_not_depend_on_these::__googletest_macro_matches_pattern!($($tt)*)
}
};
}
/// An alias for [`matches_pattern`][crate::matchers::matches_pattern!].
#[macro_export]
#[doc(hidden)]
macro_rules! __pat {
($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) }
}
#[doc(hidden)]
pub mod internal {
use crate::matcher::{Matcher, MatcherBase};
use std::fmt::Debug;
pub use ::googletest_macro::__googletest_macro_matches_pattern;
// Specialized implementation of the `predicate` matcher to support ref binding
// mode for `matches_pattern`.
pub fn pattern_only<T>(
matcher_function: fn(&T) -> bool,
match_description: &'static str,
no_match_description: &'static str,
) -> PatternOnlyMatcher<T> {
PatternOnlyMatcher { matcher_function, match_description, no_match_description }
}
#[derive(MatcherBase)]
#[doc(hidden)]
pub struct PatternOnlyMatcher<T> {
matcher_function: fn(&T) -> bool,
match_description: &'static str,
no_match_description: &'static str,
}
impl<'a, T: Debug> Matcher<&'a T> for PatternOnlyMatcher<T> {
fn matches(&self, actual: &'a T) -> crate::matcher::MatcherResult {
(self.matcher_function)(actual).into()
}
fn describe(
&self,
matcher_result: crate::matcher::MatcherResult,
) -> crate::description::Description {
match matcher_result {
crate::matcher::MatcherResult::Match => self.match_description.into(),
crate::matcher::MatcherResult::NoMatch => self.no_match_description.into(),
}
}
}
impl<T: Debug + Copy> Matcher<T> for PatternOnlyMatcher<T> {
fn matches(&self, actual: T) -> crate::matcher::MatcherResult {
(self.matcher_function)(&actual).into()
}
fn describe(
&self,
matcher_result: crate::matcher::MatcherResult,
) -> crate::description::Description {
match matcher_result {
crate::matcher::MatcherResult::Match => self.match_description.into(),
crate::matcher::MatcherResult::NoMatch => self.no_match_description.into(),
}
}
}
/// A matcher that ensures that the passed-in function compiles with the
/// benefit of inference from the value being tested. The passed-in
/// matcher is what is actually invoked for matching.
///
/// It forwards all description responsibilities to the passed-in matcher.
pub fn compile_assert_and_match<T, M>(
must_compile_function: fn(&T),
matcher: M,
) -> CompileAssertAndMatch<T, M> {
CompileAssertAndMatch { must_compile_function, matcher }
}
#[derive(MatcherBase)]
#[doc(hidden)]
pub struct CompileAssertAndMatch<T, M> {
#[allow(dead_code)]
must_compile_function: fn(&T),
matcher: M,
}
impl<'a, T: Debug, M> Matcher<&'a T> for CompileAssertAndMatch<T, M>
where
M: Matcher<&'a T>,
{
fn matches(&self, actual: &'a T) -> crate::matcher::MatcherResult {
self.matcher.matches(actual)
}
fn describe(
&self,
matcher_result: crate::matcher::MatcherResult,
) -> crate::description::Description {
self.matcher.describe(matcher_result)
}
fn explain_match(&self, actual: &'a T) -> crate::description::Description {
self.matcher.explain_match(actual)
}
}
impl<T: Debug + Copy, M> Matcher<T> for CompileAssertAndMatch<T, M>
where
M: Matcher<T>,
{
fn matches(&self, actual: T) -> crate::matcher::MatcherResult {
self.matcher.matches(actual)
}
fn describe(
&self,
matcher_result: crate::matcher::MatcherResult,
) -> crate::description::Description {
self.matcher.describe(matcher_result)
}
fn explain_match(&self, actual: T) -> crate::description::Description {
self.matcher.explain_match(actual)
}
}
}
mod compile_fail_tests {
/// ```compile_fail
/// use ::googletest::prelude::*;
/// #[derive(Debug)]
/// struct Foo { a: u32 }
/// impl Foo {
/// fn b() {}
/// }
/// let actual = Foo { a: 1 };
/// verify_that!(actual, matches_pattern!(Foo { a: eq(&1), b(): _ }));
/// ```
fn _underscore_unsupported_for_methods() {}
/// ```compile_fail
/// use ::googletest::prelude::*;
/// #[derive(Debug)]
/// struct Foo { a: u32, b: u32 }
/// let actual = Foo { a: 1, b: 2 };
/// verify_that!(actual, matches_pattern!(Foo { a: eq(&1), .., }));
/// ```
fn _dot_dot_supported_only_at_end_of_struct_pattern() {}
/// ```compile_fail
/// use ::googletest::prelude::*;
/// #[derive(Debug)]
/// struct Foo(u32, u32);
/// let actual = Foo(1, 2);
/// verify_that!(actual, matches_pattern!(Foo(eq(&1), .., )));
/// ```
fn _dot_dot_supported_only_at_end_of_tuple_struct_pattern() {}
/// ```compile_fail
/// use ::googletest::prelude::*;
/// #[derive(Debug)]
/// struct Foo { a: u32, b: u32 }
/// let actual = Foo { a: 1, b: 2 };
/// verify_that!(actual, matches_pattern!(Foo { a: eq(&1) }));
/// ```
fn _unexhaustive_struct_field_check_requires_dot_dot() {}
/// ```compile_fail
/// use ::googletest::prelude::*;
/// #[derive(Debug)]
/// enum Foo {
/// Bar { a: u32, b: u32 }
/// }
/// let actual = Foo::Bar { a: 1, b: 2 };
/// verify_that!(actual, matches_pattern!(Foo::Bar { a: eq(&1) }));
/// ```
fn _unexhaustive_enum_struct_field_check_requires_dot_dot() {}
/// ```compile_fail
/// use ::googletest::prelude::*;
/// #[derive(Debug)]
/// struct Foo(u32, u32, u32);
/// let actual = Foo(1, 2, 3);
/// verify_that!(actual, matches_pattern!(Foo(eq(&1), eq(&2) )));
/// ```
fn _unexhaustive_tuple_struct_field_check_requires_dot_dot() {}
/// ```compile_fail
/// use ::googletest::prelude::*;
/// verify_that!(1, matches_pattern!(UndefinedSymbol { }));
/// ```
fn _should_fail_to_compile_unknown_struct_with_no_fields() {}
/// ```compile_fail
/// use ::googletest::prelude::*;
/// verify_that!(1, matches_pattern!(UndefinedSymbol { a: 1 }));
/// ```
fn _should_fail_to_compile_unknown_struct_with_field() {}
/// ```compile_fail
/// use ::googletest::prelude::*;
/// verify_that!(1, matches_pattern!(UndefinedSymbol { .. }));
/// ```
fn _should_fail_to_compile_unknown_struct_with_dot_dot() {}
/// ```compile_fail
/// use ::googletest::prelude::*;
/// verify_that!(1, matches_pattern!(UndefinedSymbol( .. )));
/// ```
fn _should_fail_to_compile_unknown_tuple_struct_with_dot_dot() {}
}
@@ -0,0 +1,203 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use regex::Regex;
use std::fmt::Debug;
use std::ops::Deref;
/// Matches a string the entirety of which which matches the given regular
/// expression.
///
/// This is similar to [`contains_regex`][crate::matchers::contains_regex],
/// except that the match must cover the whole string and not a substring.
///
/// Both the actual value and the expected regular expression may be either a
/// `String` or a string reference.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass_1() -> Result<()> {
/// verify_that!("Some value", matches_regex("S.*e"))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
/// verify_that!("Another value", matches_regex("Some"))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// verify_that!("Some value", matches_regex("Some"))?; // Fails
/// # Ok(())
/// # }
/// # fn should_pass_2() -> Result<()> {
/// verify_that!("Some value".to_string(), matches_regex(".*v.*e"))?; // Passes
/// verify_that!("Some value", matches_regex(".*v.*e".to_string()))?; // Passes
/// # Ok(())
/// # }
/// # should_pass_1().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// # should_pass_2().unwrap();
/// ```
///
/// Panics if the given `pattern` is not a syntactically valid regular
/// expression.
// N.B. This returns the concrete type rather than an impl Matcher so that it
// can act simultaneously as a Matcher<str> and a Matcher<String>. Otherwise the
// compiler treats it as a Matcher<str> only and the code
// verify_that!("Some value".to_string(), matches_regex(".*value"))?;
// doesn't compile.
pub fn matches_regex<PatternT: Deref<Target = str>>(
pattern: PatternT,
) -> MatchesRegexMatcher<PatternT> {
let adjusted_pattern = format!("^{}$", pattern.deref());
let regex = Regex::new(adjusted_pattern.as_str()).unwrap();
MatchesRegexMatcher { regex, pattern, _adjusted_pattern: adjusted_pattern }
}
/// A matcher matching a string-like type matching a given regular expression.
///
/// Intended only to be used from the function [`matches_regex`] only.
/// Should not be referenced by code outside this library.
#[derive(MatcherBase)]
pub struct MatchesRegexMatcher<PatternT: Deref<Target = str>> {
regex: Regex,
pattern: PatternT,
_adjusted_pattern: String,
}
impl<PatternT, ActualT> Matcher<ActualT> for MatchesRegexMatcher<PatternT>
where
PatternT: Deref<Target = str>,
ActualT: AsRef<str> + Debug + Copy,
{
fn matches(&self, actual: ActualT) -> MatcherResult {
self.regex.is_match(actual.as_ref()).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("matches the regular expression {:#?}", self.pattern.deref()).into()
}
MatcherResult::NoMatch => {
format!("doesn't match the regular expression {:#?}", self.pattern.deref()).into()
}
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
#[test]
fn matches_regex_matches_string_reference_with_pattern() -> Result<()> {
let matcher = matches_regex("S.*e");
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn matches_regex_does_not_match_string_without_pattern() -> Result<()> {
let matcher = matches_regex("Another");
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn matches_regex_does_not_match_string_only_beginning_of_which_matches() -> Result<()> {
let matcher = matches_regex("Some");
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn matches_regex_does_not_match_string_only_end_of_which_matches() -> Result<()> {
let matcher = matches_regex("value");
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn matches_regex_matches_owned_string_with_pattern() -> Result<()> {
let matcher = matches_regex(".*value");
let result = matcher.matches(&"Some value".to_string());
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn matches_regex_matches_string_when_regex_has_beginning_of_string_marker() -> Result<()> {
let matcher = matches_regex("^Some value");
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn matches_regex_matches_string_when_regex_has_end_of_string_marker() -> Result<()> {
let matcher = matches_regex("Some value$");
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn matches_regex_matches_string_when_regex_has_both_end_markers() -> Result<()> {
let matcher = matches_regex("^Some value$");
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn matches_regex_matches_string_reference_with_owned_string() -> Result<()> {
let matcher = matches_regex(".*value".to_string());
let result = matcher.matches("Some value");
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn verify_that_works_with_owned_string() -> Result<()> {
verify_that!("Some value".to_string(), matches_regex(".*value"))
}
#[test]
fn matches_regex_displays_quoted_debug_of_pattern() -> Result<()> {
let matcher = matches_regex("\n");
verify_that!(
Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("matches the regular expression \"\\n\""))
)
}
}
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! All built-in matchers of this crate are in submodules of this module.
mod all_matcher;
mod any_matcher;
mod anything_matcher;
mod bool_matcher;
mod char_count_matcher;
mod conjunction_matcher;
mod container_eq_matcher;
mod contains_matcher;
mod contains_regex_matcher;
mod derefs_to_matcher;
mod disjunction_matcher;
mod display_matcher;
mod each_matcher;
mod elements_are_matcher;
mod empty_matcher;
mod eq_matcher;
mod err_matcher;
mod field_matcher;
mod ge_matcher;
mod gt_matcher;
mod has_entry_matcher;
mod is_encoded_string_matcher;
mod is_finite_matcher;
mod is_infinite_matcher;
mod is_matcher;
mod is_nan_matcher;
mod le_matcher;
mod len_matcher;
mod lt_matcher;
mod matches_pattern;
mod matches_regex_matcher;
mod ne_matcher;
mod near_matcher;
mod none_matcher;
mod not_matcher;
mod ok_matcher;
mod points_to_matcher;
mod pointwise_matcher;
mod predicate_matcher;
mod property_matcher;
mod result_of_matcher;
mod some_matcher;
mod str_matcher;
mod subset_of_matcher;
mod superset_of_matcher;
mod tuple_matcher;
mod unordered_elements_are_matcher;
pub use anything_matcher::anything;
pub use bool_matcher::{is_false, is_true};
pub use char_count_matcher::char_count;
pub use container_eq_matcher::container_eq;
pub use contains_matcher::{contains, ContainsMatcher};
pub use contains_regex_matcher::contains_regex;
pub use derefs_to_matcher::derefs_to;
pub use display_matcher::displays_as;
pub use each_matcher::each;
pub use empty_matcher::is_empty;
pub use eq_matcher::{eq, EqMatcher};
pub use err_matcher::err;
pub use ge_matcher::ge;
pub use gt_matcher::gt;
pub use has_entry_matcher::has_entry;
pub use is_encoded_string_matcher::is_utf8_string;
pub use is_finite_matcher::is_finite;
pub use is_infinite_matcher::is_infinite;
pub use is_nan_matcher::is_nan;
pub use le_matcher::le;
pub use len_matcher::len;
pub use lt_matcher::lt;
pub use matches_regex_matcher::matches_regex;
pub use ne_matcher::ne;
pub use near_matcher::{approx_eq, near, NearMatcher};
pub use none_matcher::none;
pub use not_matcher::not;
pub use ok_matcher::ok;
pub use points_to_matcher::points_to;
pub use predicate_matcher::{predicate, PredicateMatcher};
pub use some_matcher::some;
pub use str_matcher::{
contains_substring, ends_with, starts_with, StrMatcher, StrMatcherConfigurator,
};
pub use subset_of_matcher::subset_of;
pub use superset_of_matcher::superset_of;
// Reexport and unmangle the macros.
#[doc(inline)]
pub use crate::{
__all as all, __any as any, __contains_each as contains_each, __elements_are as elements_are,
__field as field, __is_contained_in as is_contained_in, __matches_pattern as matches_pattern,
__pat as pat, __pointwise as pointwise, __property as property, __result_of as result_of,
__result_of_ref as result_of_ref, __unordered_elements_are as unordered_elements_are,
};
// Types and functions used by macros matchers.
// Do not use directly.
// We may perform incompatible changes without major release. These elements
// should only be used through their respective macros.
#[doc(hidden)]
pub mod __internal_unstable_do_not_depend_on_these {
pub use super::conjunction_matcher::ConjunctionMatcher;
pub use super::disjunction_matcher::DisjunctionMatcher;
pub use super::elements_are_matcher::internal::ElementsAre;
pub use super::field_matcher::internal::field_matcher;
pub use super::is_matcher::is;
pub use super::matches_pattern::internal::{
__googletest_macro_matches_pattern, compile_assert_and_match, pattern_only,
};
pub use super::pointwise_matcher::internal::PointwiseMatcher;
pub use super::property_matcher::internal::{property_matcher, property_ref_matcher};
pub use super::result_of_matcher::internal::{result_of, result_of_ref};
pub use super::unordered_elements_are_matcher::internal::UnorderedElementsAreMatcher;
pub use crate::matcher_support::match_matrix::internal::Requirements;
}
@@ -0,0 +1,108 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a value is not equal (in the sense of `!=`) to `expected`.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(0, ne(1))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!(0, ne(0))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
pub fn ne<T>(expected: T) -> NEMatcher<T> {
NEMatcher { expected }
}
#[derive(MatcherBase)]
pub struct NEMatcher<T> {
expected: T,
}
impl<T: Debug, A: Debug + Copy + PartialEq<T>> Matcher<A> for NEMatcher<T> {
fn matches(&self, actual: A) -> MatcherResult {
(actual != self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!("is not equal to {:?}", self.expected).into(),
MatcherResult::NoMatch => format!("is equal to {:?}", self.expected).into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn ne_matches_i32_with_i32() -> Result<()> {
verify_that!(123, ne(234))
}
#[test]
fn ne_matches_string_reference_with_string_reference() -> Result<()> {
verify_that!("A string", ne("B string"))
}
#[test]
fn ne_matches_owned_string_with_string_reference() -> Result<()> {
let value = "A string".to_string();
verify_that!(value, ne("B string"))
}
#[test]
fn ne_matches_owned_string_reference_with_string_reference() -> Result<()> {
let value = "A string".to_string();
verify_that!(&value, ne("B string"))
}
#[test]
fn ne_struct_debug_diff() -> Result<()> {
#[derive(Debug, PartialEq)]
struct Strukt {
int: i32,
string: String,
}
let result = verify_that!(
Strukt { int: 123, string: "something".into() },
ne(&Strukt { int: 123, string: "something".into() })
);
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
Expected: is not equal to Strukt { int: 123, string: \"something\" }
Actual: Strukt { int: 123, string: \"something\" },
which is equal to Strukt { int: 123, string: \"something\" }
"})))
)
}
}
@@ -0,0 +1,363 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use num_traits::{Float, FloatConst};
use std::{borrow::Borrow, fmt::Debug};
/// Matches a value equal within `max_abs_error` of `expected`.
///
/// The type `T` of the actual, `expected`, and `max_abs_error` values must
/// implement [`Float`].
///
/// The values `expected` and `max_abs_error` may not be NaN. The value
/// `max_abs_error` must be non-negative. The matcher panics on construction
/// otherwise.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass_1() -> Result<()> {
/// verify_that!(1.0, near(1.0, 0.1))?; // Passes
/// verify_that!(1.01, near(1.0, 0.1))?; // Passes
/// verify_that!(1.25, near(1.0, 0.25))?; // Passes
/// verify_that!(0.75, near(1.0, 0.25))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
/// verify_that!(1.101, near(1.0, 0.1))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// verify_that!(0.899, near(1.0, 0.1))?; // Fails
/// # Ok(())
/// # }
/// # fn should_pass_2() -> Result<()> {
/// verify_that!(100.25, near(100.0, 0.25))?; // Passes
/// # Ok(())
/// # }
/// # should_pass_1().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// # should_pass_2().unwrap();
/// ```
///
/// The default behaviour for special values is consistent with the IEEE
/// floating point standard. Thus infinity is infinitely far away from any
/// floating point value:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_fail_1() -> Result<()> {
/// verify_that!(f64::INFINITY, near(0.0, f64::MAX))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// verify_that!(0.0, near(f64::INFINITY, f64::MAX))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_3() -> Result<()> {
/// verify_that!(f64::INFINITY, near(f64::INFINITY, f64::MAX))?; // Fails
/// # Ok(())
/// # }
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// # should_fail_3().unwrap_err();
/// ```
///
/// Similarly, by default, `NaN` is infinitely far away from any value:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_fail_1() -> Result<()> {
/// verify_that!(f64::NAN, near(0.0, f64::MAX))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// verify_that!(0.0, near(f64::NAN, f64::MAX))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_3() -> Result<()> {
/// verify_that!(f64::NAN, near(f64::NAN, f64::MAX))?; // Fails
/// # Ok(())
/// # }
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// # should_fail_3().unwrap_err();
/// ```
///
/// To treat two `NaN` values as equal, use the method
/// [`NearMatcher::nans_are_equal`].
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(f64::NAN, near(f64::NAN, f64::MAX).nans_are_equal())?; // Passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
#[track_caller]
pub fn near<T: Debug + Float + Copy>(expected: T, max_abs_error: T) -> NearMatcher<T> {
if max_abs_error.is_nan() {
panic!("max_abs_error must not be NaN");
}
if max_abs_error < T::zero() {
panic!("max_abs_error must be non-negative");
}
NearMatcher { expected, max_abs_error, nans_are_equal: false }
}
/// Matches a value approximately equal to `expected`.
///
/// This automatically computes a tolerance from the magnitude of `expected` and
/// matches any actual value within this tolerance of the expected value. The
/// tolerance is chosen to account for the inaccuracies in most ordinary
/// floating point calculations.
///
/// Otherwise this works analogously to [`near`]; see its documentation for
/// further notes.
pub fn approx_eq<T: Debug + Float + FloatConst + Copy>(expected: T) -> NearMatcher<T> {
// The FloatConst trait doesn't offer 2 as a constant but does offer 1.
let five_bits_of_mantissa = (T::one() + T::one()).powi(5);
let abs_tolerance = five_bits_of_mantissa * T::epsilon();
let max_abs_error = T::max(expected.abs() * abs_tolerance, abs_tolerance);
NearMatcher { expected, max_abs_error, nans_are_equal: false }
}
/// A matcher which matches floating-point numbers approximately equal to its
/// expected value.
#[derive(MatcherBase)]
pub struct NearMatcher<T: Debug> {
expected: T,
max_abs_error: T,
nans_are_equal: bool,
}
impl<T: Debug> NearMatcher<T> {
/// Configures this instance to treat two NaNs as equal.
///
/// This behaviour differs from the IEEE standad for floating point which
/// treats two NaNs as infinitely far apart.
pub fn nans_are_equal(mut self) -> Self {
self.nans_are_equal = true;
self
}
/// Configures this instance to treat two NaNs as not equal.
///
/// This behaviour complies with the IEEE standad for floating point. It is
/// the default behaviour for this matcher, so invoking this method is
/// usually redunant.
pub fn nans_are_not_equal(mut self) -> Self {
self.nans_are_equal = false;
self
}
}
impl<T: Borrow<F> + Debug + Copy, F: Debug + Float> Matcher<T> for NearMatcher<F> {
fn matches(&self, actual: T) -> MatcherResult {
if self.nans_are_equal && self.expected.is_nan() && actual.borrow().is_nan() {
return MatcherResult::Match;
}
let delta = *actual.borrow() - self.expected;
if delta >= -self.max_abs_error && delta <= self.max_abs_error {
MatcherResult::Match
} else {
MatcherResult::NoMatch
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("is within {:?} of {:?}", self.max_abs_error, self.expected).into()
}
MatcherResult::NoMatch => {
format!("isn't within {:?} of {:?}", self.max_abs_error, self.expected).into()
}
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
#[test]
fn matches_value_inside_range() -> Result<()> {
let matcher = near(1.0f64, 0.1f64);
let result = matcher.matches(1.0f64);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn matches_value_at_low_end_of_range() -> Result<()> {
let matcher = near(1.0f64, 0.1f64);
let result = matcher.matches(0.9f64);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn matches_value_at_high_end_of_range() -> Result<()> {
let matcher = near(1.0f64, 0.25f64);
let result = matcher.matches(1.25f64);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn does_not_match_value_below_low_end_of_range() -> Result<()> {
let matcher = near(1.0f64, 0.1f64);
let result = matcher.matches(0.899999f64);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn does_not_match_value_above_high_end_of_range() -> Result<()> {
let matcher = near(1.0f64, 0.1f64);
let result = matcher.matches(1.100001f64);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn nan_is_not_near_a_number() -> Result<()> {
let matcher = near(0.0f64, f64::MAX);
let result = matcher.matches(f64::NAN);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn nan_is_not_near_nan_by_default() -> Result<()> {
verify_that!(f64::NAN, not(near(f64::NAN, f64::MAX)))
}
#[test]
fn nan_is_not_near_nan_when_explicitly_configured() -> Result<()> {
verify_that!(f64::NAN, not(near(f64::NAN, f64::MAX).nans_are_not_equal()))
}
#[test]
fn nan_is_near_nan_if_nans_are_equal() -> Result<()> {
verify_that!(f64::NAN, near(f64::NAN, f64::MAX).nans_are_equal())
}
#[test]
fn nan_is_not_near_number_when_nans_are_equal() -> Result<()> {
verify_that!(f64::NAN, not(near(0.0, f64::MAX).nans_are_equal()))
}
#[test]
fn number_is_not_near_nan_when_nans_are_equal() -> Result<()> {
verify_that!(0.0, not(near(f64::NAN, f64::MAX).nans_are_equal()))
}
#[test]
fn inf_is_not_near_inf() -> Result<()> {
let matcher = near(f64::INFINITY, f64::MAX);
let result = matcher.matches(f64::INFINITY);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn inf_is_not_near_a_number() -> Result<()> {
let matcher = near(f64::INFINITY, f64::MAX);
let result = matcher.matches(f64::MIN);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn any_two_numbers_are_within_inf_of_each_other() -> Result<()> {
let matcher = near(f64::MIN, f64::INFINITY);
let result = matcher.matches(f64::MAX);
verify_that!(result, eq(MatcherResult::Match))
}
#[::core::prelude::v1::test]
#[should_panic]
fn panics_if_max_abs_error_is_nan() {
near(0.0, f64::NAN);
}
#[::core::prelude::v1::test]
#[should_panic]
fn panics_if_tolerance_is_negative() {
near(0.0, -1.0);
}
#[test]
fn approx_eq_matches_equal_number() -> Result<()> {
verify_that!(1.0f64, approx_eq(1.0f64))
}
#[test]
fn approx_eq_matches_really_close_f64_number() -> Result<()> {
verify_that!(1.0f64, approx_eq(1.0 + 16.0 * f64::EPSILON))
}
#[test]
fn approx_eq_matches_really_close_f64_number_to_large_number() -> Result<()> {
verify_that!(1000f64, approx_eq(1000.0 + 16000.0 * f64::EPSILON))
}
#[test]
fn approx_eq_matches_really_close_f64_number_to_zero() -> Result<()> {
verify_that!(16.0 * f64::EPSILON, approx_eq(0.0))
}
#[test]
fn approx_eq_matches_really_close_f32_number() -> Result<()> {
verify_that!(1.0f32, approx_eq(1.0 + 16.0 * f32::EPSILON))
}
#[test]
fn approx_eq_does_not_match_distant_number() -> Result<()> {
verify_that!(0.0f64, not(approx_eq(1.0f64)))
}
#[test]
fn approx_eq_supports_ref() -> Result<()> {
verify_that!(&0.0f64, approx_eq(0.0f64))
}
#[test]
fn approx_eq_supports_container_matchers() -> Result<()> {
verify_that!(
vec![1., 2., 3.],
unordered_elements_are![approx_eq(2.), approx_eq(3.), approx_eq(1.)]
)
}
}
@@ -0,0 +1,129 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::fmt::Debug;
/// Matches an `Option` containing `None`.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(None::<()>, none())?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!(Some("Some value"), none())?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
pub fn none() -> NoneMatcher {
NoneMatcher
}
#[derive(MatcherBase)]
pub struct NoneMatcher;
impl<T: Debug + Copy> Matcher<Option<T>> for NoneMatcher {
fn matches(&self, actual: Option<T>) -> MatcherResult {
actual.is_none().into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => "is none".into(),
MatcherResult::NoMatch => "is some(_)".into(),
}
}
}
impl<'a, T: Debug> Matcher<&'a Option<T>> for NoneMatcher {
fn matches(&self, actual: &'a Option<T>) -> MatcherResult {
actual.is_none().into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => "is none".into(),
MatcherResult::NoMatch => "is some(_)".into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
#[test]
fn none_matches_option_with_none() -> Result<()> {
let matcher = none();
let result = matcher.matches(None::<i32>);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn none_does_not_match_option_with_value() -> Result<()> {
let matcher = none();
let result = matcher.matches(Some(0));
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn none_matches_option_by_ref() -> Result<()> {
verify_that!(None::<String>, none())
}
#[test]
fn none_does_not_match_option_with_value_by_ref() -> Result<()> {
verify_that!(Some("123".to_string()), not(none()))
}
#[test]
fn none_describe_match_option_by_ref() -> Result<()> {
verify_that!(
Matcher::<&Option<String>>::describe(&none(), MatcherResult::Match),
displays_as(eq("is none"))
)
}
#[test]
fn none_describe_no_match_option_by_ref() -> Result<()> {
verify_that!(
Matcher::<&Option<String>>::describe(&none(), MatcherResult::NoMatch),
displays_as(eq("is some(_)"))
)
}
#[test]
fn none_describe_match_option() -> Result<()> {
verify_that!(
Matcher::<Option<i32>>::describe(&none(), MatcherResult::Match),
displays_as(eq("is none"))
)
}
#[test]
fn none_describe_no_match_option() -> Result<()> {
verify_that!(
Matcher::<Option<i32>>::describe(&none(), MatcherResult::NoMatch),
displays_as(eq("is some(_)"))
)
}
}
@@ -0,0 +1,105 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches the actual value exactly when the inner matcher does _not_ match.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(0, not(eq(1)))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// verify_that!(0, not(eq(0)))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
pub fn not<InnerMatcherT>(inner: InnerMatcherT) -> NotMatcher<InnerMatcherT> {
NotMatcher { inner }
}
#[derive(MatcherBase)]
pub struct NotMatcher<InnerMatcherT> {
inner: InnerMatcherT,
}
impl<T: Debug + Copy, InnerMatcherT: Matcher<T>> Matcher<T> for NotMatcher<InnerMatcherT> {
fn matches(&self, actual: T) -> MatcherResult {
match self.inner.matches(actual) {
MatcherResult::Match => MatcherResult::NoMatch,
MatcherResult::NoMatch => MatcherResult::Match,
}
}
fn explain_match(&self, actual: T) -> Description {
self.inner.explain_match(actual)
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
self.inner.describe(if matcher_result.into() {
MatcherResult::NoMatch
} else {
MatcherResult::Match
})
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn matches_when_inner_matcher_does_not_match() -> Result<()> {
let matcher = not(eq(1));
let result = matcher.matches(0);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn does_not_match_when_inner_matcher_matches() -> Result<()> {
let matcher = not(eq(1));
let result = matcher.matches(1);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn match_explanation_references_actual_value() -> Result<()> {
let result = verify_that!(&[1], not(container_eq([1])));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Actual: [1],
which contains all the elements
"
))))
)
}
}
@@ -0,0 +1,291 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a `Result` containing `Ok` with a value matched by `inner`.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> googletest::Result<()> {
/// verify_that!(Ok::<_, ()>("Some value"), ok(eq("Some value")))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> googletest::Result<()> {
/// verify_that!(Err::<&str, _>("An error"), ok(eq("An error")))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_2() -> googletest::Result<()> {
/// verify_that!(Ok::<_, ()>("Some value"), ok(eq("Some other value")))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
pub fn ok<InnerMatcherT>(inner: InnerMatcherT) -> OkMatcher<InnerMatcherT> {
OkMatcher { inner }
}
#[derive(MatcherBase)]
pub struct OkMatcher<InnerMatcherT> {
inner: InnerMatcherT,
}
impl<T: Debug + Copy, E: Debug + Copy, InnerMatcherT: Matcher<T>> Matcher<std::result::Result<T, E>>
for OkMatcher<InnerMatcherT>
{
fn matches(&self, actual: std::result::Result<T, E>) -> MatcherResult {
actual.map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
}
fn explain_match(&self, actual: std::result::Result<T, E>) -> Description {
match actual {
Ok(o) => {
Description::new().text("which is a success").nested(self.inner.explain_match(o))
}
Err(_) => "which is an error".into(),
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!(
"is a success containing a value, which {}",
self.inner.describe(MatcherResult::Match)
)
.into(),
MatcherResult::NoMatch => format!(
"is an error or a success containing a value, which {}",
self.inner.describe(MatcherResult::NoMatch)
)
.into(),
}
}
}
impl<'a, T: Debug, E: Debug, InnerMatcherT: Matcher<&'a T>> Matcher<&'a std::result::Result<T, E>>
for OkMatcher<InnerMatcherT>
{
fn matches(&self, actual: &'a std::result::Result<T, E>) -> MatcherResult {
actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
}
fn explain_match(&self, actual: &'a std::result::Result<T, E>) -> Description {
match actual {
Ok(o) => {
Description::new().text("which is a success").nested(self.inner.explain_match(o))
}
Err(_) => "which is an error".into(),
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!(
"is a success containing a value, which {}",
self.inner.describe(MatcherResult::Match)
)
.into(),
MatcherResult::NoMatch => format!(
"is an error or a success containing a value, which {}",
self.inner.describe(MatcherResult::NoMatch)
)
.into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn ok_matches_result_with_value() -> Result<()> {
let matcher = ok(eq(1));
let value: std::result::Result<i32, i32> = Ok(1);
let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn ok_does_not_match_result_with_wrong_value() -> Result<()> {
let matcher = ok(eq(1));
let value: std::result::Result<i32, i32> = Ok(0);
let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn ok_does_not_match_result_with_err() -> Result<()> {
let matcher = ok(eq(1));
let value: std::result::Result<i32, i32> = Err(1);
let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn ok_matches_result_with_value_by_ref() -> Result<()> {
let result: std::result::Result<String, String> = Ok("123".into());
verify_that!(result, ok(eq("123")))
}
#[test]
fn ok_does_not_match_result_with_wrong_value_by_ref() -> Result<()> {
let result: std::result::Result<String, String> = Ok("321".into());
verify_that!(result, not(ok(eq("123"))))
}
#[test]
fn ok_does_not_match_result_with_err_by_ref() -> Result<()> {
let result: std::result::Result<String, String> = Err("123".into());
verify_that!(result, not(ok(eq("123"))))
}
#[test]
fn ok_full_error_message() -> Result<()> {
let result = verify_that!(Ok::<i32, i32>(1), ok(eq(2)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: Ok::<i32, i32>(1)
Expected: is a success containing a value, which is equal to 2
Actual: Ok(1),
which is a success
which isn't equal to 2
"
))))
)
}
#[test]
fn ok_describe_match() -> Result<()> {
let matcher = ok(eq(1));
verify_that!(
Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is a success containing a value, which is equal to 1"))
)
}
#[test]
fn ok_describe_no_match() -> Result<()> {
let matcher = ok(eq(1));
verify_that!(
Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::NoMatch),
displays_as(eq("is an error or a success containing a value, which isn't equal to 1"))
)
}
#[test]
fn ok_describe_match_by_ref() -> Result<()> {
let matcher = ok(eq(&1));
verify_that!(
Matcher::<&std::result::Result<i32, String>>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is a success containing a value, which is equal to 1"))
)
}
#[test]
fn ok_describe_no_match_by_ref() -> Result<()> {
let matcher = ok(eq(&1));
verify_that!(
Matcher::<&std::result::Result<i32, String>>::describe(
&matcher,
MatcherResult::NoMatch
),
displays_as(eq("is an error or a success containing a value, which isn't equal to 1"))
)
}
#[test]
fn ok_explain_match_ok_success() -> Result<()> {
let actual = Ok(1);
let matcher = ok(eq(1));
verify_that!(
Matcher::<std::result::Result<i32, i32>>::explain_match(&matcher, actual),
displays_as(eq("which is a success\n which is equal to 1"))
)
}
#[test]
fn ok_explain_match_ok_fail() -> Result<()> {
let actual = Ok(1);
let matcher = ok(eq(2));
verify_that!(
Matcher::<std::result::Result<i32, i32>>::explain_match(&matcher, actual),
displays_as(eq("which is a success\n which isn't equal to 2"))
)
}
#[test]
fn ok_explain_match_ok_err() -> Result<()> {
let actual = Err(1);
let matcher = ok(eq(2));
verify_that!(
Matcher::<std::result::Result<i32, i32>>::explain_match(&matcher, actual),
displays_as(eq("which is an error"))
)
}
#[test]
fn ok_explain_match_ok_success_by_ref() -> Result<()> {
let actual = Ok("123".to_string());
let matcher = ok(eq("123"));
verify_that!(
Matcher::<&std::result::Result<String, String>>::explain_match(&matcher, &actual),
displays_as(eq("which is a success\n which is equal to \"123\""))
)
}
#[test]
fn ok_explain_match_ok_fail_by_ref() -> Result<()> {
let actual = Ok("321".to_string());
let matcher = ok(eq("123"));
verify_that!(
Matcher::<&std::result::Result<String, String>>::explain_match(&matcher, &actual),
displays_as(eq("which is a success\n which isn't equal to \"123\""))
)
}
#[test]
fn ok_explain_match_ok_err_by_ref() -> Result<()> {
let actual = Err("123".to_string());
let matcher = ok(eq("123"));
verify_that!(
Matcher::<&std::result::Result<String, String>>::explain_match(&matcher, &actual),
displays_as(eq("which is an error"))
)
}
}
@@ -0,0 +1,88 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::fmt::Debug;
/// Matches a reference pointing to a value matched by the [`Matcher`]
/// `expected`.
///
/// This is useful for combining matchers, especially when working with
/// iterators.
///
/// For example:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(&123, points_to(eq(123)))?;
/// verify_that!(vec![1,2,3], each(points_to(gt(0))))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
pub fn points_to<MatcherT>(expected: MatcherT) -> PointsToMatcher<MatcherT> {
PointsToMatcher { expected }
}
#[derive(MatcherBase)]
pub struct PointsToMatcher<MatcherT> {
expected: MatcherT,
}
impl<'a, ExpectedT, MatcherT> Matcher<&'a ExpectedT> for PointsToMatcher<MatcherT>
where
ExpectedT: Debug + Copy,
MatcherT: Matcher<ExpectedT>,
{
fn matches(&self, actual: &'a ExpectedT) -> MatcherResult {
self.expected.matches(*actual)
}
fn explain_match(&self, actual: &'a ExpectedT) -> Description {
self.expected.explain_match(*actual)
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
self.expected.describe(matcher_result)
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn points_to_matches_ref() -> Result<()> {
verify_that!(&123, points_to(eq(123)))
}
#[test]
fn match_explanation_references_actual_value() -> Result<()> {
let result = verify_that!(&1, points_to(eq(0)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Actual: 1,
which isn't equal to 0
"
))))
)
}
}
@@ -0,0 +1,232 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module; the declarative
// macro is documented in the matcher module.
#![doc(hidden)]
/// Generates a matcher which matches a container each of whose elements match
/// the given matcher name applied respectively to each element of the given
/// container.
///
/// For example, the following matches a container of integers each of which
/// does not exceed the given upper bounds:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value = vec![1, 2, 3];
/// verify_that!(value, pointwise!(le, [&1, &2, &3]))?; // Passes
/// verify_that!(value, pointwise!(|e| points_to(le(e)), [1, 2, 3]))?; // Passes
/// verify_that!(value, pointwise!(|e| points_to(le(e)), vec![1, 3, 3]))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// # let value = vec![1, 2, 3];
/// verify_that!(value, pointwise!(|e| points_to(le(e)), [1, 1, 3]))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
/// One can also use a closure which returns a matcher:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value = vec![1.00001, 2.000001, 3.00001];
/// verify_that!(value, pointwise!(|v| points_to(near(v, 0.001)), [1.0, 2.0, 3.0]))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// One can pass up to three containers to supply arguments to the function
/// creating the matcher:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value = vec![1.00001, 2.000001, 3.00001];
/// verify_that!(value, pointwise!(|v, t| points_to(near(v, t)), [1.0, 2.0, 3.0], [0.001, 0.0001, 0.01]))?;
/// verify_that!(
/// value,
/// pointwise!(
/// |v, t, u| points_to(near(v, t * u)),
/// [1.0, 2.0, 3.0],
/// [0.001, 0.0001, 0.01],
/// [0.5, 0.5, 1.0]
/// )
/// )?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// When using `pointwise!` with multiple containers, the caller must ensure
/// that all of the containers have the same size. This matcher does not check
/// whether the sizes match.
///
/// The actual value must be a container such as a `&Vec`, an array, or a
/// slice. More precisely, the actual value must implement [`IntoIterator`].
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value = vec![1, 2, 3];
/// verify_that!(value, pointwise!(|i| points_to(le(i)), [1, 3, 3]))?; // Passes
/// verify_that!([1, 2, 3], pointwise!(le, [1, 3, 3]))?; // Passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// The second argument can be any value implementing `IntoIterator`, such as a
/// `Vec` or an array. The container does not have to have the same type as the
/// actual value, but the value type must be the same.
///
/// **Note for users of the [`Pointwise`] matcher in C++ GoogleTest:**
///
/// This macro differs from `Pointwise` in that the first parameter is not a
/// matcher which matches a pair but rather the name of a function of one
/// argument whose output is a matcher. This means that one can use standard
/// matchers like `eq`, `le`, and so on with `pointwise!` but certain C++ tests
/// using `Pointwise` will require some extra work to port.
///
/// [`IntoIterator`]: std::iter::IntoIterator
/// [`Iterator`]: std::iter::Iterator
/// [`Iterator::collect`]: std::iter::Iterator::collect
/// [`Pointwise`]: https://google.github.io/googletest/reference/matchers.html#container-matchers
/// [`Vec`]: std::vec::Vec
#[macro_export]
#[doc(hidden)]
macro_rules! __pointwise {
($matcher:expr, $container:expr) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
$container.into_iter().map($matcher).collect(),
)
}};
($matcher:expr, $left_container:expr, $right_container:expr) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
$left_container
.into_iter()
.zip($right_container.into_iter())
.map(|(l, r)| $matcher(l, r))
.collect(),
)
}};
($matcher:expr, $left_container:expr, $middle_container:expr, $right_container:expr) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
$left_container
.into_iter()
.zip($right_container.into_iter().zip($middle_container.into_iter()))
.map(|(l, (m, r))| $matcher(l, m, r))
.collect(),
)
}};
}
/// Module for use only by the procedural macros in this module.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
pub mod internal {
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::zipped_iterator::zip;
use std::fmt::Debug;
/// This struct is meant to be used only through the `pointwise` macro.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
#[derive(MatcherBase)]
pub struct PointwiseMatcher<MatcherT> {
matchers: Vec<MatcherT>,
}
impl<MatcherT> PointwiseMatcher<MatcherT> {
pub fn new(matchers: Vec<MatcherT>) -> Self {
Self { matchers }
}
}
impl<T: Debug + Copy, MatcherT: Matcher<T>, ContainerT: Copy + Debug> Matcher<ContainerT>
for PointwiseMatcher<MatcherT>
where
ContainerT: IntoIterator<Item = T>,
{
fn matches(&self, actual: ContainerT) -> MatcherResult {
let mut zipped_iterator = zip(actual.into_iter(), self.matchers.iter());
for (element, matcher) in zipped_iterator.by_ref() {
if matcher.matches(element).is_no_match() {
return MatcherResult::NoMatch;
}
}
if zipped_iterator.has_size_mismatch() {
MatcherResult::NoMatch
} else {
MatcherResult::Match
}
}
fn explain_match(&self, actual: ContainerT) -> Description {
// TODO(b/260819741) This code duplicates elements_are_matcher.rs. Consider
// extract as a separate library. (or implement pointwise! with
// elements_are)
let actual_iterator = actual.into_iter();
let mut zipped_iterator = zip(actual_iterator, self.matchers.iter());
let mut mismatches = Vec::new();
for (idx, (a, e)) in zipped_iterator.by_ref().enumerate() {
if e.matches(a).is_no_match() {
mismatches.push(format!("element #{idx} is {a:?}, {}", e.explain_match(a)));
}
}
if mismatches.is_empty() {
if !zipped_iterator.has_size_mismatch() {
"which matches all elements".into()
} else {
format!(
"which has size {} (expected {})",
zipped_iterator.left_size(),
self.matchers.len()
)
.into()
}
} else if mismatches.len() == 1 {
format!("where {}", mismatches[0]).into()
} else {
let mismatches = mismatches.into_iter().collect::<Description>();
format!("where:\n{}", mismatches.bullet_list().indent()).into()
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"{} elements satisfying respectively:\n{}",
if matcher_result.into() { "has" } else { "doesn't have" },
self.matchers
.iter()
.map(|m| m.describe(MatcherResult::Match))
.collect::<Description>()
.enumerate()
.indent()
)
.into()
}
}
}
@@ -0,0 +1,215 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Creates a matcher based on the predicate provided.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(3, predicate(|x: i32| x % 2 == 1))?; // Passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// The predicate should take the subject type and return a
/// boolean.
///
/// Note: even if the Rust compiler should be able to infer the type of
/// the closure argument, it is likely that it won't.
/// See <https://github.com/rust-lang/rust/issues/12679> for update on this issue.
/// This is easily fixed by explicitly declaring the type of the argument
pub fn predicate<P>(predicate: P) -> PredicateMatcher<P, NoDescription, NoDescription> {
PredicateMatcher {
predicate,
positive_description: NoDescription,
negative_description: NoDescription,
}
}
impl<P> PredicateMatcher<P, NoDescription, NoDescription> {
/// Configures this instance to provide a more meaningful description.
///
/// For example, to make sure the error message is more useful
///
/// ```
/// # use googletest::matchers::{predicate, PredicateMatcher};
/// # let _ =
/// predicate(|x: i32| x % 2 == 1)
/// .with_description("is odd", "is even")
/// # ;
/// ```
///
/// This is optional as it only provides value when the test fails.
///
/// Description can be passed by `&str`, `String` or `Fn() -> Into<String>`.
pub fn with_description<D1: PredicateDescription, D2: PredicateDescription>(
self,
positive_description: D1,
negative_description: D2,
) -> PredicateMatcher<P, D1, D2> {
PredicateMatcher { predicate: self.predicate, positive_description, negative_description }
}
}
/// A matcher which applies `predicate` on the value.
///
/// See [`predicate`].
#[derive(MatcherBase)]
pub struct PredicateMatcher<P, D1, D2> {
predicate: P,
positive_description: D1,
negative_description: D2,
}
/// A trait to allow [`PredicateMatcher::with_description`] to accept multiple
/// types.
///
/// See [`PredicateMatcher::with_description`]
pub trait PredicateDescription {
fn to_description(&self) -> Description;
}
impl PredicateDescription for &str {
fn to_description(&self) -> Description {
self.to_string().into()
}
}
impl PredicateDescription for String {
fn to_description(&self) -> Description {
self.to_string().into()
}
}
impl<T, S> PredicateDescription for T
where
T: Fn() -> S,
S: Into<String>,
{
fn to_description(&self) -> Description {
self().into().into()
}
}
// Sentinel type to tag a MatcherBuilder as without a description.
#[doc(hidden)]
pub struct NoDescription;
impl<T: Debug + Copy, P> Matcher<T> for PredicateMatcher<P, NoDescription, NoDescription>
where
P: Fn(T) -> bool,
{
fn matches(&self, actual: T) -> MatcherResult {
(self.predicate)(actual).into()
}
fn describe(&self, result: MatcherResult) -> Description {
match result {
MatcherResult::Match => "matches".into(),
MatcherResult::NoMatch => "does not match".into(),
}
}
}
impl<T: Debug + Copy, P, D1: PredicateDescription, D2: PredicateDescription> Matcher<T>
for PredicateMatcher<P, D1, D2>
where
P: Fn(T) -> bool,
{
fn matches(&self, actual: T) -> MatcherResult {
(self.predicate)(actual).into()
}
fn describe(&self, result: MatcherResult) -> Description {
match result {
MatcherResult::Match => self.positive_description.to_description(),
MatcherResult::NoMatch => self.negative_description.to_description(),
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
// Simple matcher with a description
fn is_odd() -> impl Matcher<i32> {
predicate(|x| x % 2 == 1).with_description("is odd", "is even")
}
#[test]
fn predicate_matcher_odd() -> Result<()> {
verify_that!(1, is_odd())
}
#[test]
fn predicate_matcher_odd_explain_match_matches() -> Result<()> {
verify_that!(is_odd().explain_match(1), displays_as(eq("which is odd")))
}
#[test]
fn predicate_matcher_odd_explain_match_does_not_match() -> Result<()> {
verify_that!(is_odd().explain_match(2), displays_as(eq("which is even")))
}
// Simple Matcher without description
fn is_even() -> impl Matcher<i32> {
predicate(|x| x % 2 == 0)
}
#[test]
fn predicate_matcher_even() -> Result<()> {
verify_that!(2, is_even())
}
#[test]
fn predicate_matcher_even_explain_match_matches() -> Result<()> {
verify_that!(is_even().explain_match(2), displays_as(eq("which matches")))
}
#[test]
fn predicate_matcher_even_explain_match_does_not_match() -> Result<()> {
verify_that!(is_even().explain_match(1), displays_as(eq("which does not match")))
}
#[test]
fn predicate_matcher_generator_lambda() -> Result<()> {
let is_divisible_by = |quotient| {
predicate(move |x: i32| x % quotient == 0).with_description(
move || format!("is divisible by {quotient}"),
move || format!("is not divisible by {quotient}"),
)
};
verify_that!(49, is_divisible_by(7))
}
#[test]
fn predicate_matcher_inline() -> Result<()> {
verify_that!(2048, predicate(|x: i32| x.count_ones() == 1))
}
#[test]
fn predicate_matcher_function_pointer() -> Result<()> {
use std::time::Duration;
verify_that!(&Duration::new(0, 0), predicate(Duration::is_zero))
}
}
@@ -0,0 +1,384 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module; the declarative
// macro is documented in the matcher module.
#![doc(hidden)]
/// Matches an object which, upon calling the given method on it with the given
/// arguments, produces a value matched by the given inner matcher.
///
/// This is particularly useful as a nested matcher when the desired
/// property cannot be accessed through a field and must instead be
/// extracted through a method call. For example:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// pub struct MyStruct {
/// a_field: u32,
/// }
///
/// impl MyStruct {
/// pub fn get_a_field(&self) -> u32 { self.a_field }
/// }
///
/// let value = vec![MyStruct { a_field: 100 }];
/// verify_that!(value, contains(property!(&MyStruct.get_a_field(), eq(100))))
/// # .unwrap();
/// ```
///
///
/// If the inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// pub struct MyStruct {
/// a_field: u32,
/// }
///
/// impl MyStruct {
/// pub fn get_a_field(&self) -> u32 { self.a_field }
/// }
///
/// let value = vec![MyStruct { a_field: 100 }];
/// verify_that!(value, contains(property!(&MyStruct.get_a_field(), 100)))
/// # .unwrap();
/// ```
///
/// **Important**: The method should be pure function with a deterministic
/// output and no side effects. In particular, in the event of an assertion
/// failure, it will be invoked a second time, with the assertion failure output
/// reflecting the *second* invocation.
///
/// The method may also take additional litteral arguments:
///
/// ```
/// # use googletest::prelude::*;
/// # #[derive(Debug)]
/// # pub struct MyStruct {
/// # a_field: u32,
/// # }
/// impl MyStruct {
/// pub fn add_to_a_field(&self, a: u32) -> u32 { self.a_field + a }
/// }
///
/// # let value = vec![MyStruct { a_field: 100 }];
/// verify_that!(value, contains(property!(&MyStruct.add_to_a_field(50), eq(150))))
/// # .unwrap();
/// ```
///
/// The arguments must be litteral as `property!` is not able to capture them.
///
/// # Specification of the property pattern
///
/// The specification of the field follow the syntax: `(ref)? (&)?
/// $TYPE.$PROPERTY\($ARGUMENT\)`.
///
/// The `&` allows to specify whether this matcher matches against an actual of
/// type `$TYPE` (`$TYPE` must implement `Copy`) or a `&$TYPE`.
///
/// For instance:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// pub struct AStruct;
///
/// impl AStruct {
/// fn a_property(&self) -> i32 {32}
/// }
/// # fn should_pass() -> Result<()> {
/// verify_that!(AStruct, property!(&AStruct.a_property(), eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug, Clone, Copy)]
/// pub struct AStruct;
///
/// impl AStruct {
/// fn a_property(self) -> i32 {32}
/// }
/// # fn should_pass() -> Result<()> {
/// verify_that!(AStruct, property!(AStruct.a_property(), eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// The `ref` allows to bind the property returned value by reference, which is
/// required if the field type does not implement `Copy`.
///
/// For instance:
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// pub struct AStruct;
///
/// impl AStruct {
/// fn a_property(&self) -> i32 {32}
/// }
/// # fn should_pass() -> Result<()> {
/// verify_that!(AStruct, property!(&AStruct.a_property(), eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// If `property!` is qualified by both `&` and `ref`, they can both be omitted.
///
/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
/// pub struct AStruct;
///
/// impl AStruct {
/// fn a_property(&self) -> String {"32".into()}
/// }
/// # fn should_pass() -> Result<()> {
/// verify_that!(AStruct, property!(&AStruct.a_property(), ref eq("32")))?;
/// verify_that!(AStruct, property!(AStruct.a_property(), eq("32")))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// This macro is analogous to [`field`][crate::matchers::field], except that it
/// extracts the datum to be matched from the given object by invoking a method
/// rather than accessing a field.
///
/// The list of arguments may optionally have a trailing comma.
#[macro_export]
#[doc(hidden)]
macro_rules! __property {
($($t:tt)*) => { $crate::property_internal!($($t)*) }
}
// Internal-only macro created so that the macro definition does not appear in
// generated documentation.
#[doc(hidden)]
#[macro_export]
macro_rules! property_internal {
// `ref` variant.
(
// The 3 cases of `$(& $($_amp:literal)?)?` -> `$(& $($_amp)*)*`
// 1. outer group captures nothing => expansion produces nothing
// 2. outer group captures just `&` => expansion produces `&`
// 3. outer group captures `& <literal>` => disallowed by `@assert_empty` subrule invocation
//
// `$_amp:literal` works only because the following `$t:ident` or `::` can't be captured by
// it.
$(& $($_amp:literal)?)? // Optional `&`'s presence is implicitly captured by `_amp`.
$(:: $($_cs:literal)?)? // Optional `::`'s presence is implicitly captured by `_cs`.
$($t:ident)::+ $(::<$($t_ty_args:ty),* $(,)?>)?
.$method:tt $(::<$($m_ty_args:ty),* $(,)?>)? ($($argument:expr),* $(,)?),
ref $m:expr
) => {{
$crate::property_internal!(@assert_empty $($($_amp)*)* $($($_cs)*)*);
$crate::property_internal!(
@self_arg
struct_type: [$(& $($_amp)*)* $(:: $($_cs)*)* $($t)::+ $( <$($t_ty_args),*>)*]
method_prefix: [ $(:: $($_cs)*)* $($t)::+ $(::<$($t_ty_args),*>)*]
[$method $(::<$($m_ty_args),*>)*] [$($argument),*] [$m])
}};
// Non-`ref` variant.
(
// See comment on previous variant above.
$(& $($_amp:literal)?)? // Optional `&`'s presence is implicitly captured by `_amp`.
$(:: $($_cs:literal)?)? // Optional `::`'s presence is implicitly captured by `_cs`.
$($t:ident)::+ $(::<$($t_ty_args:ty),* $(,)?>)?
.$method:tt $(::<$($m_ty_args:ty),* $(,)?>)? ($($argument:expr),* $(,)?),
$m:expr) => {{
$crate::property_internal!(@assert_empty $($($_amp)*)* $($($_cs)*)*);
$crate::property_internal!(
@self_dot
struct_type: [$(& $($_amp)*)* $(:: $($_cs)*)* $($t)::+ $(<$($t_ty_args),*>)*]
[$method $(::<$($m_ty_args),*>)*] [$($argument),*] [$m])
}};
(@assert_empty) => {};
(@assert_empty $($l:literal)+) => {
compile_error!("property! argument must start with an optional `&` followed by a path")
};
(@self_arg struct_type: [$struct_ty:ty]
method_prefix: [$($method_prefix:tt)+]
[$($method:tt)*] [$($argument:expr),*] [$m:expr]) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher(
|o: $struct_ty| $($method_prefix)*::$($method)* (o, $($argument),*),
&stringify!($($method)* ($($argument),*)),
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
}};
(@self_dot struct_type: [$struct_ty:ty]
[$($method:tt)*] [$($argument:expr),*] [$m:expr]) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher(
|o: &$struct_ty| o.$($method)* ($($argument),*),
&stringify!($($method)* ($($argument),*)),
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
}};
}
/// Items for use only by the declarative macros in this module.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
pub mod internal {
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
pub fn property_matcher<OuterT: Debug, InnerT: Debug, MatcherT>(
extractor: fn(&OuterT) -> InnerT,
property_desc: &'static str,
inner: MatcherT,
) -> PropertyMatcher<OuterT, InnerT, MatcherT> {
PropertyMatcher { extractor, property_desc, inner }
}
#[derive(MatcherBase)]
pub struct PropertyMatcher<OuterT, InnerT, MatcherT> {
extractor: fn(&OuterT) -> InnerT,
property_desc: &'static str,
inner: MatcherT,
}
impl<InnerT, OuterT, MatcherT> Matcher<OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
where
InnerT: Debug + Copy,
OuterT: Debug + Copy,
MatcherT: Matcher<InnerT>,
{
fn matches(&self, actual: OuterT) -> MatcherResult {
self.inner.matches((self.extractor)(&actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"has property `{}`, which {}",
self.property_desc,
self.inner.describe(matcher_result)
)
.into()
}
fn explain_match(&self, actual: OuterT) -> Description {
let actual_inner = (self.extractor)(&actual);
format!(
"whose property `{}` is `{:#?}`, {}",
self.property_desc,
actual_inner,
self.inner.explain_match(actual_inner)
)
.into()
}
}
impl<'a, InnerT, OuterT, MatcherT> Matcher<&'a OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
where
InnerT: Debug,
OuterT: Debug,
MatcherT: for<'b> Matcher<&'b InnerT>,
{
fn matches(&self, actual: &'a OuterT) -> MatcherResult {
self.inner.matches(&(self.extractor)(actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"has property `{}`, which {}",
self.property_desc,
self.inner.describe(matcher_result)
)
.into()
}
fn explain_match(&self, actual: &'a OuterT) -> Description {
let actual_inner = (self.extractor)(actual);
format!(
"whose property `{}` is `{:#?}`, {}",
self.property_desc,
actual_inner,
self.inner.explain_match(&actual_inner)
)
.into()
}
}
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
pub fn property_ref_matcher<OuterT, InnerT, ExtractorT, MatcherT>(
extractor: ExtractorT,
property_desc: &'static str,
inner: MatcherT,
) -> PropertyRefMatcher<ExtractorT, MatcherT>
where
OuterT: Debug,
InnerT: Debug,
MatcherT: for<'a> Matcher<&'a InnerT>,
ExtractorT: Fn(OuterT) -> InnerT,
{
PropertyRefMatcher { extractor, property_desc, inner }
}
#[derive(MatcherBase)]
pub struct PropertyRefMatcher<ExtractorT, MatcherT> {
extractor: ExtractorT,
property_desc: &'static str,
inner: MatcherT,
}
impl<
InnerT: Debug,
OuterT: Debug + Copy,
MatcherT: for<'a> Matcher<&'a InnerT>,
ExtractorT: Fn(OuterT) -> InnerT,
> Matcher<OuterT> for PropertyRefMatcher<ExtractorT, MatcherT>
{
fn matches(&self, actual: OuterT) -> MatcherResult {
self.inner.matches(&(self.extractor)(actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
format!(
"has property `{}`, which {}",
self.property_desc,
self.inner.describe(matcher_result)
)
.into()
}
fn explain_match(&self, actual: OuterT) -> Description {
let actual_inner = (self.extractor)(actual);
format!(
"whose property `{}` is `{:#?}`, {}",
self.property_desc,
actual_inner,
self.inner.explain_match(&actual_inner)
)
.into()
}
}
}
@@ -0,0 +1,441 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// Matches a value where the result of `callable` applied to the value matches
/// the inner matcher.
///
/// The `callable` will be called twice, so make sure it is pure.
/// ```
/// use googletest::prelude::*;
/// fn should_pass() -> googletest::Result<()> {
/// verify_that!(100, result_of!(|value| value + 1, eq(101)))?; // Passes
/// Ok(())
/// }
///
/// fn should_fail() -> googletest::Result<()> {
/// verify_that!(100, result_of!(|value| value * 2, eq(100)))?; // Fails
/// Ok(())
/// }
/// should_pass().unwrap();
/// should_fail().unwrap_err();
/// ```
#[macro_export]
macro_rules! __result_of {
($function: expr, $matcher: expr) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::result_of(
$function,
$matcher,
stringify!($function),
)
}};
}
/// Matches a value where the reference to the result of `callable` applied to
/// the value matches the inner matcher.
///
/// The `callable` will be called twice, so make sure it is pure.
/// ```
/// use googletest::prelude::*;
/// fn should_pass_1() -> googletest::Result<()> {
/// verify_that!("hello", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))?; // Passes
/// Ok(())
/// }
///
/// fn should_pass_2() -> googletest::Result<()> {
/// verify_that!(100, result_of_ref!(|value| value + 1, eq(&101)))?; // Passes
/// Ok(())
/// }
///
/// fn should_fail() -> googletest::Result<()> {
/// verify_that!("world", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))?; // Passes
/// Ok(())
/// }
/// should_pass_1().unwrap();
/// should_pass_2().unwrap();
/// should_fail().unwrap_err();
/// ```
#[macro_export]
macro_rules! __result_of_ref {
($function: expr, $matcher: expr) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::result_of_ref(
$function,
$matcher,
stringify!($function),
)
}};
}
/// Items for use only by the declarative macros in this module.
///
/// **For internal use only. API stability is not guaranteed!**
#[doc(hidden)]
pub mod internal {
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::fmt::Debug;
pub fn result_of<Callable, InnerMatcher>(
callable: Callable,
inner_matcher: InnerMatcher,
callable_description: &'static str,
) -> ResultOfMatcher<Callable, InnerMatcher> {
ResultOfMatcher { callable, inner_matcher, callable_description }
}
#[derive(MatcherBase)]
pub struct ResultOfMatcher<Callable, InnerMatcher> {
callable: Callable,
inner_matcher: InnerMatcher,
callable_description: &'static str,
}
impl<I: Copy + Debug, T: Debug + Copy, CallableT: Fn(I) -> T, InnerMatcherT: Matcher<T>>
Matcher<I> for ResultOfMatcher<CallableT, InnerMatcherT>
{
fn matches(&self, actual: I) -> MatcherResult {
self.inner_matcher.matches((self.callable)(actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
Description::new()
.text(format!("by applying {},", self.callable_description))
.nested(self.inner_matcher.describe(matcher_result))
}
fn explain_match(&self, actual: I) -> Description {
let actual_result = (self.callable)(actual);
Description::new()
.text(format!("which, results into {actual_result:?}",))
.nested(self.describe(self.matches(actual)))
}
}
pub fn result_of_ref<Callable, InnerMatcher>(
callable: Callable,
inner_matcher: InnerMatcher,
callable_description: &'static str,
) -> ResultOfRefMatcher<Callable, InnerMatcher> {
ResultOfRefMatcher { callable, inner_matcher, callable_description }
}
#[derive(MatcherBase)]
pub struct ResultOfRefMatcher<Callable, InnerMatcher> {
callable: Callable,
inner_matcher: InnerMatcher,
callable_description: &'static str,
}
impl<
I: Copy + Debug,
T: Debug,
Callable: Fn(I) -> T,
InnerMatcherT: for<'a> Matcher<&'a T>,
> Matcher<I> for ResultOfRefMatcher<Callable, InnerMatcherT>
{
fn matches(&self, actual: I) -> MatcherResult {
self.inner_matcher.matches(&(self.callable)(actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
Description::new()
.text(format!("by applying {},", self.callable_description))
.nested(self.inner_matcher.describe(matcher_result))
}
fn explain_match(&self, actual: I) -> Description {
let actual_result = (self.callable)(actual);
Description::new()
.text(format!("which, results into {actual_result:?}",))
.nested(self.describe(self.matches(actual)))
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn result_of_match_with_value() -> Result<()> {
verify_that!(1, result_of!(|value| value + 1, eq(2)))
}
#[test]
fn result_of_match_with_value_function() -> Result<()> {
fn inc_by_one(value: i32) -> i32 {
value + 1
}
verify_that!(1, result_of!(inc_by_one, eq(2)))
}
#[test]
fn result_of_match_with_different_value() -> Result<()> {
let result = verify_that!(0, result_of!(|value| value - 1, eq(2)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 0
Expected: by applying |value| value - 1,
is equal to 2
Actual: 0,
which, results into -1
by applying |value| value - 1,
isn't equal to 2
"
))))
)
}
#[test]
fn result_of_match_with_different_value_block_closure() -> Result<()> {
let result = verify_that!(0, result_of!(|value| { value - 1 }, eq(2)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 0
Expected: by applying |value| { value - 1 },
is equal to 2
Actual: 0,
which, results into -1
by applying |value| { value - 1 },
isn't equal to 2
"
))))
)
}
#[test]
fn result_of_match_with_different_value_multiline_closure() -> Result<()> {
let result = verify_that!(
0,
result_of!(
|value| {
let dec = value - 1;
let inc = dec + 1;
inc - 2
},
eq(2)
)
);
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 0
Expected: by applying |value| { let dec = value - 1; let inc = dec + 1; inc - 2 },
is equal to 2
Actual: 0,
which, results into -2
by applying |value| { let dec = value - 1; let inc = dec + 1; inc - 2 },
isn't equal to 2
"
))))
)
}
#[test]
fn result_of_match_with_different_value_function() -> Result<()> {
fn dec_by_one(value: i32) -> i32 {
value - 1
}
let result = verify_that!(0, result_of!(dec_by_one, eq(2)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: 0
Expected: by applying dec_by_one,
is equal to 2
Actual: 0,
which, results into -1
by applying dec_by_one,
isn't equal to 2
"
))))
)
}
#[test]
fn result_of_ref_match_with_string_reference() -> Result<()> {
verify_that!("hello", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))
}
#[test]
fn result_of_ref_match_with_string_reference_function() -> Result<()> {
fn to_upper_case<S: AsRef<str>>(s: S) -> String {
s.as_ref().to_uppercase()
}
verify_that!("hello", result_of_ref!(to_upper_case, eq("HELLO")))
}
#[test]
fn result_of_ref_match_with_copy_types() -> Result<()> {
verify_that!(100, result_of_ref!(|value| value + 1, eq(&101)))
}
#[test]
fn result_of_ref_match_with_different_value() -> Result<()> {
let result = verify_that!("world", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
r#"
Value of: "world"
Expected: by applying |s: &str| s.to_uppercase(),
is equal to "HELLO"
Actual: "world",
which, results into "WORLD"
by applying |s: &str| s.to_uppercase(),
isn't equal to "HELLO""#
))))
)
}
#[test]
fn result_of_ref_match_with_different_value_block_closure() -> Result<()> {
let result =
verify_that!("world", result_of_ref!(|s: &str| { s.to_uppercase() }, eq("HELLO")));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
r#"
Value of: "world"
Expected: by applying |s: &str| { s.to_uppercase() },
is equal to "HELLO"
Actual: "world",
which, results into "WORLD"
by applying |s: &str| { s.to_uppercase() },
isn't equal to "HELLO"
"#
))))
)
}
#[test]
fn result_of_ref_match_with_different_value_function() -> Result<()> {
fn to_upper_case<S: AsRef<str>>(s: S) -> String {
s.as_ref().to_uppercase()
}
let result = verify_that!("world", result_of_ref!(to_upper_case, eq("HELLO")));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
r#"
Value of: "world"
Expected: by applying to_upper_case,
is equal to "HELLO"
Actual: "world",
which, results into "WORLD"
by applying to_upper_case,
isn't equal to "HELLO"
"#
))))
)
}
#[test]
fn result_of_ref_match_different_with_closure_variable() -> Result<()> {
let to_upper_case = |s: &str| s.to_uppercase();
let result = verify_that!("world", result_of_ref!(to_upper_case, eq("HELLO")));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
r#"
Value of: "world"
Expected: by applying to_upper_case,
is equal to "HELLO"
Actual: "world",
which, results into "WORLD"
by applying to_upper_case,
isn't equal to "HELLO"
"#
))))
)
}
#[test]
fn result_of_ref_match_different_with_method_literal() -> Result<()> {
let result = verify_that!("world", result_of_ref!(str::to_uppercase, eq("HELLO")));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
r#"
Value of: "world"
Expected: by applying str::to_uppercase,
is equal to "HELLO"
Actual: "world",
which, results into "WORLD"
by applying str::to_uppercase,
isn't equal to "HELLO"
"#
))))
)
}
#[test]
fn result_of_ref_match_different_with_function_return_closure() -> Result<()> {
fn upper_case() -> impl Fn(&str) -> String {
|s: &str| s.to_uppercase()
}
let result = verify_that!("world", result_of_ref!(upper_case(), eq("HELLO")));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
r#"
Value of: "world"
Expected: by applying upper_case(),
is equal to "HELLO"
Actual: "world",
which, results into "WORLD"
by applying upper_case(),
isn't equal to "HELLO"
"#
))))
)
}
#[test]
fn test_describe_simple() -> Result<()> {
let matcher = result_of!(|x| x + 1, eq(2));
let description = matcher.describe(matcher.matches(0));
verify_that!(
description,
displays_as(eq(indoc!(
r#"
by applying |x| x + 1,
isn't equal to 2"#
)))
)
}
#[test]
fn test_describe_complicated() -> Result<()> {
let matcher = result_of_ref!(
|s: &str| s.chars().collect::<Vec<_>>(),
each(predicate(char::is_ascii_alphabetic))
);
let description = matcher.describe(matcher.matches("A quick brown fox"));
verify_that!(
description,
displays_as(eq(indoc!(
r#"
by applying |s: &str| s.chars().collect::<Vec<_>>(),
contains no element that matches"#
)))
)
}
}
@@ -0,0 +1,250 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches an `Option` containing a value matched by `inner`.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(Some("Some value"), some(eq("Some value")))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
/// verify_that!(None::<&str>, some(eq("Some value")))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// verify_that!(Some("Some value"), some(eq("Some other value")))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
pub fn some<Inner>(inner: Inner) -> SomeMatcher<Inner> {
SomeMatcher { inner }
}
#[derive(MatcherBase)]
pub struct SomeMatcher<InnerMatcherT> {
inner: InnerMatcherT,
}
impl<T: Debug + Copy, InnerMatcherT: Matcher<T>> Matcher<Option<T>> for SomeMatcher<InnerMatcherT> {
fn matches(&self, actual: Option<T>) -> MatcherResult {
actual.map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
}
fn explain_match(&self, actual: Option<T>) -> Description {
match (self.matches(actual), actual) {
(_, Some(t)) => {
Description::new().text("which has a value").nested(self.inner.explain_match(t))
}
(_, None) => "which is None".into(),
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("has a value which {}", self.inner.describe(MatcherResult::Match)).into()
}
MatcherResult::NoMatch => format!(
"is None or has a value which {}",
self.inner.describe(MatcherResult::NoMatch)
)
.into(),
}
}
}
impl<'a, T: Debug, InnerMatcherT: Matcher<&'a T>> Matcher<&'a Option<T>>
for SomeMatcher<InnerMatcherT>
{
fn matches(&self, actual: &'a Option<T>) -> MatcherResult {
actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
}
fn explain_match(&self, actual: &'a Option<T>) -> Description {
match (self.matches(actual), actual) {
(_, Some(t)) => {
Description::new().text("which has a value").nested(self.inner.explain_match(t))
}
(_, None) => "which is None".into(),
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
format!("has a value which {}", self.inner.describe(MatcherResult::Match)).into()
}
MatcherResult::NoMatch => format!(
"is None or has a value which {}",
self.inner.describe(MatcherResult::NoMatch)
)
.into(),
}
}
}
#[cfg(test)]
mod tests {
use crate::matcher::MatcherResult;
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
#[test]
fn some_matches_option_with_value() -> Result<()> {
let matcher = some(eq(1));
let result = matcher.matches(Some(1));
verify_that!(result, eq(MatcherResult::Match))
}
#[test]
fn some_does_not_match_option_with_wrong_value() -> Result<()> {
let matcher = some(eq(1));
let result = matcher.matches(Some(0));
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn some_does_not_match_option_with_none() -> Result<()> {
let matcher = some(eq(1));
let result = matcher.matches(None::<i32>);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn some_matches_option_with_by_ref_value() -> Result<()> {
verify_that!(Some("123".to_string()), some(eq("123")))
}
#[test]
fn some_does_not_match_option_with_wrong_by_ref_value() -> Result<()> {
verify_that!(Some("321".to_string()), not(some(eq("123"))))
}
#[test]
fn some_does_not_match_option_with_by_ref_none() -> Result<()> {
verify_that!(None::<String>, not(some(eq("123"))))
}
#[test]
fn some_full_error_message() -> Result<()> {
let result = verify_that!(Some(2), some(eq(1)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: Some(2)
Expected: has a value which is equal to 1
Actual: Some(2),
which has a value
which isn't equal to 1
"
))))
)
}
#[test]
fn some_describe_matches() -> Result<()> {
verify_that!(
Matcher::<Option<i32>>::describe(&some(eq(1)), MatcherResult::Match),
displays_as(eq("has a value which is equal to 1"))
)
}
#[test]
fn some_describe_does_not_match() -> Result<()> {
verify_that!(
Matcher::<Option<i32>>::describe(&some(eq(1)), MatcherResult::NoMatch),
displays_as(eq("is None or has a value which isn't equal to 1"))
)
}
#[test]
fn some_describe_matches_of_by_ref() -> Result<()> {
verify_that!(
Matcher::<Option<&String>>::describe(&some(eq("123")), MatcherResult::Match),
displays_as(eq("has a value which is equal to \"123\""))
)
}
#[test]
fn some_describe_does_not_match_of_by_ref() -> Result<()> {
verify_that!(
Matcher::<Option<&String>>::describe(&some(eq("123")), MatcherResult::NoMatch),
displays_as(eq("is None or has a value which isn't equal to \"123\""))
)
}
#[test]
fn some_explain_match_with_none() -> Result<()> {
verify_that!(some(eq(1)).explain_match(None::<i32>), displays_as(eq("which is None")))
}
#[test]
fn some_explain_match_with_some_success() -> Result<()> {
verify_that!(
some(eq(1)).explain_match(Some(1)),
displays_as(eq("which has a value\n which is equal to 1"))
)
}
#[test]
fn some_explain_match_with_some_fail() -> Result<()> {
verify_that!(
some(eq(1)).explain_match(Some(2)),
displays_as(eq("which has a value\n which isn't equal to 1"))
)
}
#[test]
fn some_explain_match_with_none_by_ref() -> Result<()> {
verify_that!(
some(eq("123")).explain_match(&None::<String>),
displays_as(eq("which is None"))
)
}
#[test]
fn some_explain_match_with_some_success_by_ref() -> Result<()> {
verify_that!(
some(eq("123")).explain_match(&Some("123".to_string())),
displays_as(eq("which has a value\n which is equal to \"123\""))
)
}
#[test]
fn some_explain_match_with_some_fail_by_ref() -> Result<()> {
verify_that!(
some(eq("123")).explain_match(&Some("321".to_string())),
displays_as(eq("which has a value\n which isn't equal to \"123\""))
)
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,253 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a container all of whose items are in the given container
/// `superset`.
///
/// The element type `ElementT` must implement `PartialEq` to allow element
/// comparison.
///
/// `ActualT` and `ExpectedT` can each be any container which
/// implements `IntoIterator`. For instance, `T` can be a common container like
/// `&Vec` or arrays. They need not be the same container type.
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashSet;
/// # fn should_pass_1() -> Result<()> {
/// let value = vec![1, 2, 3];
/// verify_that!(value, subset_of([&1, &2, &3, &4]))?; // Passes
/// let array_value = [1, 2, 3];
/// verify_that!(array_value, subset_of([1, 2, 3, 4]))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// # let value = vec![1, 2, 3];
/// verify_that!(value, subset_of([&1, &2]))?; // Fails: 3 is not in the superset
/// # Ok(())
/// # }
/// # should_pass_1().unwrap();
/// # should_fail().unwrap_err();
///
/// # fn should_pass_2() -> Result<()> {
/// let value: HashSet<i32> = [1, 2, 3].into();
/// verify_that!(value, subset_of([&1, &2, &3]))?; // Passes
/// # Ok(())
/// # }
/// # should_pass_2().unwrap();
/// ```
///
/// Item multiplicity in both the actual and expected containers is ignored:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value: Vec<i32> = vec![0, 0, 1];
/// verify_that!(value, subset_of([&0, &1]))?; // Passes
/// verify_that!(value, subset_of([&0, &1, &1]))?; // Passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// A note on performance: This matcher uses a naive algorithm with a worst-case
/// runtime proportional to the *product* of the sizes of the actual and
/// expected containers as well as the time to check equality of each pair of
/// items. It should not be used on especially large containers.
pub fn subset_of<ExpectedT>(superset: ExpectedT) -> SubsetOfMatcher<ExpectedT> {
SubsetOfMatcher { superset }
}
#[derive(MatcherBase)]
pub struct SubsetOfMatcher<ExpectedT> {
superset: ExpectedT,
}
impl<ElementT: Debug + PartialEq + Copy, ActualT: Debug + Copy, ExpectedT: Debug> Matcher<ActualT>
for SubsetOfMatcher<ExpectedT>
where
ActualT: IntoIterator<Item = ElementT>,
for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
{
fn matches(&self, actual: ActualT) -> MatcherResult {
for actual_item in actual {
if self.expected_is_missing(&actual_item) {
return MatcherResult::NoMatch;
}
}
MatcherResult::Match
}
fn explain_match(&self, actual: ActualT) -> Description {
let unexpected_elements = actual
.into_iter()
.enumerate()
.filter(|item| self.expected_is_missing(&item.1))
.map(|(idx, actual_item)| format!("{actual_item:#?} at #{idx}"))
.collect::<Vec<_>>();
match unexpected_elements.len() {
0 => "which no element is unexpected".into(),
1 => format!("whose element {} is unexpected", &unexpected_elements[0]).into(),
_ => format!("whose elements {} are unexpected", unexpected_elements.join(", ")).into(),
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!("is a subset of {:#?}", self.superset).into(),
MatcherResult::NoMatch => format!("isn't a subset of {:#?}", self.superset).into(),
}
}
}
impl<ElementT: PartialEq, ExpectedT> SubsetOfMatcher<ExpectedT>
where
for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
{
fn expected_is_missing(&self, needle: &ElementT) -> bool {
!self.superset.into_iter().any(|item| item == needle)
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::Result;
use indoc::indoc;
use std::collections::HashSet;
#[test]
fn subset_of_matches_empty_vec() -> Result<()> {
let value: Vec<i32> = vec![];
verify_that!(value, subset_of([]))
}
#[test]
fn subset_of_matches_vec_with_one_element() -> Result<()> {
let value = vec![1];
verify_that!(value, subset_of([&1]))
}
#[test]
fn subset_of_matches_vec_with_two_elements() -> Result<()> {
let value = vec![1, 2];
verify_that!(value, subset_of([&1, &2]))
}
#[test]
fn subset_of_matches_vec_when_expected_has_excess_element() -> Result<()> {
let value = vec![1, 2];
verify_that!(value, subset_of([&1, &2, &3]))
}
#[test]
fn subset_of_matches_vec_when_expected_has_excess_element_first() -> Result<()> {
let value = vec![1, 2];
verify_that!(value, subset_of([&3, &1, &2]))
}
#[test]
fn subset_of_matches_slice_with_one_element() -> Result<()> {
let value = &[1];
verify_that!(value, subset_of([&1]))
}
#[test]
fn subset_of_matches_hash_set_with_one_element() -> Result<()> {
let value: HashSet<i32> = [1].into();
verify_that!(value, subset_of([&1]))
}
#[test]
fn subset_of_does_not_match_when_first_element_does_not_match() -> Result<()> {
let value = vec![0];
verify_that!(value, not(subset_of([&1])))
}
#[test]
fn subset_of_does_not_match_when_second_element_does_not_match() -> Result<()> {
let value = vec![2, 0];
verify_that!(value, not(subset_of([&2])))
}
#[test]
fn subset_of_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
let result = verify_that!(vec![0, 2, 3], subset_of([&1, &2, &3]));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![0, 2, 3]
Expected: is a subset of [
1,
2,
3,
]
Actual: [0, 2, 3],
whose element 0 at #0 is unexpected
"
))))
)
}
#[test]
fn subset_of_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
let result = verify_that!(vec![1, 0, 3], subset_of([&1, &2, &3]));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![1, 0, 3]
Expected: is a subset of [
1,
2,
3,
]
Actual: [1, 0, 3],
whose element 0 at #1 is unexpected
"
))))
)
}
#[test]
fn subset_of_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
let result = verify_that!(vec![0, 0, 3], subset_of([&1, &2, &3]));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![0, 0, 3]
Expected: is a subset of [
1,
2,
3,
]
Actual: [0, 0, 3],
whose elements 0 at #0, 0 at #1 are unexpected
"
))))
)
}
}
@@ -0,0 +1,253 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
/// Matches a container containing all of the items in the given container
/// `subset`.
///
/// The element type `ElementT` must implement `PartialEq` to allow element
/// comparison.
///
/// `ActualT` and `ExpectedT` can each be any container which implements
/// `IntoIterator`. For instance, `ActualT` and `ExpectedT` can be a
/// common container like `&Vec`, arrays or slices. They need not be the same
/// container type.
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashSet;
/// # fn should_pass_1() -> Result<()> {
/// let value = vec![1, 2, 3];
/// verify_that!(value, superset_of([&1, &2]))?; // Passes
/// let array_value = [1, 2, 3];
/// verify_that!(array_value, superset_of([1, 2]))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// # let value = vec![1, 2, 3];
/// verify_that!(value, superset_of([&1, &2, &4]))?; // Fails: 4 is not in the subset
/// # Ok(())
/// # }
/// # should_pass_1().unwrap();
/// # should_fail().unwrap_err();
///
/// # fn should_pass_2() -> Result<()> {
/// let value: HashSet<i32> = [1, 2, 3].into();
/// verify_that!(value, superset_of([&1, &2, &3]))?; // Passes
/// # Ok(())
/// # }
/// # should_pass_2().unwrap();
/// ```
///
/// Item multiplicity in both the actual and expected containers is ignored:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value: Vec<i32> = vec![0, 0, 1];
/// verify_that!(value, superset_of([&0, &1]))?; // Passes
/// verify_that!(value, superset_of([&0, &1, &1]))?; // Passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
/// A note on performance: This matcher uses a naive algorithm with a worst-case
/// runtime proportional to the *product* of the sizes of the actual and
/// expected containers as well as the time to check equality of each pair of
/// items. It should not be used on especially large containers.
pub fn superset_of<ExpectedT>(subset: ExpectedT) -> SupersetOfMatcher<ExpectedT> {
SupersetOfMatcher { subset }
}
#[derive(MatcherBase)]
pub struct SupersetOfMatcher<ExpectedT> {
subset: ExpectedT,
}
impl<ElementT: Debug + Copy + PartialEq, ActualT: Debug + Copy, ExpectedT: Debug> Matcher<ActualT>
for SupersetOfMatcher<ExpectedT>
where
ActualT: IntoIterator<Item = ElementT>,
for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
{
fn matches(&self, actual: ActualT) -> MatcherResult {
for expected_item in &self.subset {
if actual_is_missing(actual, expected_item) {
return MatcherResult::NoMatch;
}
}
MatcherResult::Match
}
fn explain_match(&self, actual: ActualT) -> Description {
let missing_items: Vec<_> = self
.subset
.into_iter()
.filter(|expected_item| actual_is_missing(actual, *expected_item))
.map(|expected_item| format!("{expected_item:#?}"))
.collect();
match missing_items.len() {
0 => "whose no element is missing".into(),
1 => format!("whose element {} is missing", &missing_items[0]).into(),
_ => format!("whose elements {} are missing", missing_items.join(", ")).into(),
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!("is a superset of {:#?}", self.subset).into(),
MatcherResult::NoMatch => format!("isn't a superset of {:#?}", self.subset).into(),
}
}
}
fn actual_is_missing<ElementT: PartialEq, ActualT>(actual: ActualT, needle: &ElementT) -> bool
where
ActualT: IntoIterator<Item = ElementT>,
{
!actual.into_iter().any(|item| &item == needle)
}
#[cfg(test)]
mod tests {
use crate as googletest;
use crate::prelude::*;
use indoc::indoc;
use std::collections::HashSet;
#[test]
fn superset_of_matches_empty_vec() -> googletest::Result<()> {
let value: Vec<i32> = vec![];
verify_that!(value, superset_of([]))
}
#[test]
fn superset_of_matches_vec_with_one_element() -> googletest::Result<()> {
let value = vec![1];
verify_that!(value, superset_of([&1]))
}
#[test]
fn superset_of_matches_vec_with_two_items() -> googletest::Result<()> {
let value = vec![1, 2];
verify_that!(value, superset_of([&1, &2]))
}
#[test]
fn superset_of_matches_vec_when_actual_has_excess_element() -> googletest::Result<()> {
let value = vec![1, 2, 3];
verify_that!(value, superset_of([&1, &2]))
}
#[test]
fn superset_of_matches_vec_when_actual_has_excess_element_first() -> googletest::Result<()> {
let value = vec![3, 1, 2];
verify_that!(value, superset_of([&1, &2]))
}
#[test]
fn superset_of_matches_slice_with_one_element() -> googletest::Result<()> {
let value = &[1];
verify_that!(value, superset_of([&1]))
}
#[test]
fn superset_of_matches_hash_set_with_one_element() -> googletest::Result<()> {
let value: HashSet<i32> = [1].into();
verify_that!(value, superset_of([&1]))
}
#[test]
fn superset_of_does_not_match_when_first_element_does_not_match() -> googletest::Result<()> {
let value = vec![0];
verify_that!(value, not(superset_of([&1])))
}
#[test]
fn superset_of_does_not_match_when_second_element_does_not_match() -> googletest::Result<()> {
let value = vec![2];
verify_that!(value, not(superset_of([&2, &0])))
}
#[test]
fn superset_of_shows_correct_message_when_first_item_does_not_match() -> googletest::Result<()>
{
let result = verify_that!(vec![0, 2, 3], superset_of([&1, &2, &3]));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![0, 2, 3]
Expected: is a superset of [
1,
2,
3,
]
Actual: [0, 2, 3],
whose element 1 is missing
"
))))
)
}
#[test]
fn superset_of_shows_correct_message_when_second_item_does_not_match() -> googletest::Result<()>
{
let result = verify_that!(vec![1, 0, 3], superset_of([&1, &2, &3]));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![1, 0, 3]
Expected: is a superset of [
1,
2,
3,
]
Actual: [1, 0, 3],
whose element 2 is missing
"
))))
)
}
#[test]
fn superset_of_shows_correct_message_when_first_two_items_do_not_match(
) -> googletest::Result<()> {
let result = verify_that!(vec![0, 0, 3], superset_of([&1, &2, &3]));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
Value of: vec![0, 0, 3]
Expected: is a superset of [
1,
2,
3,
]
Actual: [0, 0, 3],
whose elements 1, 2 are missing
"
))))
)
}
}
@@ -0,0 +1,255 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module; the declarative
// macro is documented at the top level.
#![doc(hidden)]
/// Functions for use only by the declarative macros in this module.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
pub mod internal {
use crate::{
description::Description,
matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
impl MatcherBase for () {}
// This implementation is provided for completeness, but is completely trivial.
// The only actual value which can be supplied is (), which must match.
impl Matcher<()> for () {
fn matches(&self, _: ()) -> MatcherResult {
MatcherResult::Match
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => "is the empty tuple".into(),
MatcherResult::NoMatch => "is not the empty tuple".into(),
}
}
}
impl Matcher<&()> for () {
fn matches(&self, _: &()) -> MatcherResult {
MatcherResult::Match
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
<Self as Matcher<()>>::describe(self, matcher_result)
}
}
/// Generates a tuple matcher for tuples of a specific length.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
macro_rules! tuple_matcher_n {
($([$field_number:tt, $matcher_type:ident, $field_type:ident]),*) => {
impl<$($matcher_type: MatcherBase),*> MatcherBase for ($($matcher_type,)*){}
impl<$($field_type: Debug + Copy, $matcher_type: Matcher<$field_type>),*>
Matcher<($($field_type,)*)> for ($($matcher_type,)*)
{
fn matches(&self, actual: ($($field_type,)*)) -> MatcherResult {
$(match self.$field_number.matches(actual.$field_number) {
MatcherResult::Match => {},
MatcherResult::NoMatch => {
return MatcherResult::NoMatch;
}
})*
MatcherResult::Match
}
fn explain_match(&self, actual: ($($field_type,)*)) -> Description {
let mut explanation = Description::new().text("which").nested(
self.describe(self.matches(actual)));
$(match self.$field_number.matches(actual.$field_number) {
MatcherResult::Match => {},
MatcherResult::NoMatch => {
explanation = explanation
.text(format!(concat!("Element #", $field_number, " is {:?},"),
actual.$field_number))
.nested(self.$field_number.explain_match(actual.$field_number));
}
})*
explanation
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
let mut description = Description::new().text(
"is a tuple whose values respectively match:");
$(description = description.nested(
self.$field_number.describe(matcher_result));)*
description
}
MatcherResult::NoMatch => {
let mut description = Description::new().text(
"is a tuple whose values do not respectively match:");
$(description = description.nested(
self.$field_number.describe(MatcherResult::Match));)*
description
}
}
}
}
impl<'a, $($field_type: Debug, $matcher_type: Matcher<&'a $field_type>),*>
Matcher<&'a ($($field_type,)*)> for ($($matcher_type,)*)
{
fn matches(&self, actual: &'a ($($field_type,)*)) -> MatcherResult {
$(match self.$field_number.matches(&actual.$field_number) {
MatcherResult::Match => {},
MatcherResult::NoMatch => {
return MatcherResult::NoMatch;
}
})*
MatcherResult::Match
}
fn explain_match(&self, actual: &'a ($($field_type,)*)) -> Description {
let mut explanation = Description::new()
.text("which")
.nested(
Matcher::<&'a ($($field_type,)*)>::describe(
self, self.matches(actual)));
$(match self.$field_number.matches(&actual.$field_number) {
MatcherResult::Match => {},
MatcherResult::NoMatch => {
explanation = explanation
.text(format!(
concat!(
"Element #",
$field_number,
" is {:?},"),
actual.$field_number))
.nested(self.$field_number.explain_match(&actual.$field_number));
}
})*
explanation
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
let mut description = Description::new().text(
"is a tuple whose values respectively match:");
$(description = description.nested(
self.$field_number.describe(matcher_result));)*
description
}
MatcherResult::NoMatch => {
let mut description = Description::new().text(
"is a tuple whose values do not respectively match:");
$(description = description.nested(
self.$field_number.describe(MatcherResult::Match));)*
description
}
}
}
}
};
}
tuple_matcher_n!([0, I0, T0]);
tuple_matcher_n!([0, I0, T0], [1, I1, T1]);
tuple_matcher_n!([0, I0, T0], [1, I1, T1], [2, I2, T2]);
tuple_matcher_n!([0, I0, T0], [1, I1, T1], [2, I2, T2], [3, I3, T3]);
tuple_matcher_n!([0, I0, T0], [1, I1, T1], [2, I2, T2], [3, I3, T3], [4, I4, T4]);
tuple_matcher_n!([0, I0, T0], [1, I1, T1], [2, I2, T2], [3, I3, T3], [4, I4, T4], [5, I5, T5]);
tuple_matcher_n!(
[0, I0, T0],
[1, I1, T1],
[2, I2, T2],
[3, I3, T3],
[4, I4, T4],
[5, I5, T5],
[6, I6, T6]
);
tuple_matcher_n!(
[0, I0, T0],
[1, I1, T1],
[2, I2, T2],
[3, I3, T3],
[4, I4, T4],
[5, I5, T5],
[6, I6, T6],
[7, I7, T7]
);
tuple_matcher_n!(
[0, I0, T0],
[1, I1, T1],
[2, I2, T2],
[3, I3, T3],
[4, I4, T4],
[5, I5, T5],
[6, I6, T6],
[7, I7, T7],
[8, I8, T8]
);
tuple_matcher_n!(
[0, I0, T0],
[1, I1, T1],
[2, I2, T2],
[3, I3, T3],
[4, I4, T4],
[5, I5, T5],
[6, I6, T6],
[7, I7, T7],
[8, I8, T8],
[9, I9, T9]
);
tuple_matcher_n!(
[0, I0, T0],
[1, I1, T1],
[2, I2, T2],
[3, I3, T3],
[4, I4, T4],
[5, I5, T5],
[6, I6, T6],
[7, I7, T7],
[8, I8, T8],
[9, I9, T9],
[10, I10, T10]
);
tuple_matcher_n!(
[0, I0, T0],
[1, I1, T1],
[2, I2, T2],
[3, I3, T3],
[4, I4, T4],
[5, I5, T5],
[6, I6, T6],
[7, I7, T7],
[8, I8, T8],
[9, I9, T9],
[10, I10, T10],
[11, I11, T11]
);
}
@@ -0,0 +1,609 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// There are no visible documentation elements in this module; the declarative
// macro is documented in the matchers module.
#![doc(hidden)]
/// Matches a container whose elements in any order have a 1:1 correspondence
/// with the provided element matchers.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(&1), ge(&2), anything()])?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
/// verify_that!(vec![1], unordered_elements_are![eq(&1), ge(&2)])?; // Fails: container has wrong size
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(&1), ge(&4), eq(&2)])?; // Fails: second matcher not matched
/// # Ok(())
/// # }
/// # fn should_fail_3() -> Result<()> {
/// verify_that!(vec![3, 2, 1], unordered_elements_are![ge(&3), ge(&3), ge(&3)])?; // Fails: no 1:1 correspondence
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// # should_fail_3().unwrap_err();
/// ```
///
/// The actual value must be a container such as a `&Vec`, an array, or a
/// slice. More precisely, the actual value must implement [`IntoIterator`].
///
/// This can also be omitted in [`verify_that!`] macros and replaced with curly
/// brackets.
///
/// ```
/// # use googletest::prelude::*;
/// verify_that!(vec![1, 2], {eq(&2), eq(&1)})
/// # .unwrap();
/// ```
///
/// Note: This behavior is only possible in [`verify_that!`] macros. In any
/// other cases, it is still necessary to use the
/// [`unordered_elements_are!`][crate::matchers::unordered_elements_are] macro.
///
/// ```compile_fail
/// # use googletest::prelude::*;
/// verify_that!(vec![vec![1,2], vec![3]], {{eq(2), eq(1)}, {eq(3)}})
/// # .unwrap();
/// ```
///
/// Use this instead:
/// ```
/// # use googletest::prelude::*;
/// verify_that!(vec![vec![1,2], vec![3]],
/// {unordered_elements_are![eq(&2), eq(&1)], unordered_elements_are![eq(&3)]})
/// # .unwrap();
/// ```
///
/// If an inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
///
/// verify_that!(vec![1,2,3], unordered_elements_are![lt(&2), gt(&1), &3])
/// # .unwrap();
/// ```
///
/// The matcher proceeds in three stages:
///
/// 1. It first checks whether the actual value is of the right size to possibly
/// be matched by each of the given matchers. If not, then it immediately
/// fails explaining that the size is incorrect.
///
/// 2. It then checks whether each matcher matches at least one corresponding
/// element in the actual container and each element in the actual container
/// is matched by at least one matcher. If not, it fails with a message
/// indicating which matcher respectively container elements had no
/// counterparts.
///
/// 3. Finally, it checks whether the mapping of matchers to corresponding
/// actual elements is a 1-1 correspondence and fails if that is not the
/// case. The failure message then shows the best matching it could find,
/// including which matchers did not have corresponding unique elements in
/// the container and which container elements had no corresponding matchers.
///
/// [`IntoIterator`]: std::iter::IntoIterator
/// [`Iterator`]: std::iter::Iterator
/// [`Iterator::collect`]: std::iter::Iterator::collect
/// [`Vec`]: std::vec::Vec
#[macro_export]
#[doc(hidden)]
macro_rules! __unordered_elements_are {
($(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
(),
$crate::matchers::__internal_unstable_do_not_depend_on_these::
Requirements::PerfectMatch)
}};
($($matcher:expr),* $(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
($(
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
$matcher
)
,)*),
$crate::matchers::__internal_unstable_do_not_depend_on_these::
Requirements::PerfectMatch)
}};
}
/// Matches a container containing elements matched by the given matchers.
///
/// To match, each given matcher must have a corresponding element in the
/// container which it matches. There must be a mapping uniquely matching each
/// matcher to a container element. The container can, however, contain
/// additional elements that don't correspond to any matcher.
///
/// Put another way, `contains_each!` matches if there is a subset of the actual
/// container which
/// [`unordered_elements_are`][crate::matchers::unordered_elements_are] would
/// match.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(vec![3, 2, 1], contains_each![eq(&2), ge(&3)])?; // Passes
/// verify_that!(vec![3, 2, 1], contains_each![ge(&2), ge(&2)])?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
/// verify_that!(vec![1], contains_each![eq(&1), ge(&2)])?; // Fails: container too small
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// verify_that!(vec![3, 2, 1], contains_each![eq(&1), ge(&4)])?; // Fails: second matcher unmatched
/// # Ok(())
/// # }
/// # fn should_fail_3() -> Result<()> {
/// verify_that!(vec![3, 2, 1], contains_each![ge(&3), ge(&3), ge(&3)])?; // Fails: no matching
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// # should_fail_3().unwrap_err();
/// ```
///
/// The actual value must be a container such as a `&Vec`, an array, or a
/// slice. More precisely, the actual value must implement [`IntoIterator`].
///
/// If an inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
///
/// verify_that!(vec![1,2,3], contains_each![lt(&2), &3])
/// # .unwrap();
/// ```
///
/// The matcher proceeds in three stages:
///
/// 1. It first checks whether the actual value is large enough to possibly be
/// matched by each of the given matchers. If not, then it immediately fails
/// explaining that the size is too small.
///
/// 2. It then checks whether each matcher matches at least one corresponding
/// element in the actual container and fails if that is not the case. The
/// failure message indicates which matcher had no corresponding element.
///
/// 3. Finally, it checks whether the mapping of matchers to corresponding
/// actual elements is 1-1 and fails if that is not the case. The failure
/// message then shows the best matching it could find, including which
/// matchers did not have corresponding unique elements in the container.
///
/// [`IntoIterator`]: std::iter::IntoIterator
/// [`Iterator`]: std::iter::Iterator
/// [`Iterator::collect`]: std::iter::Iterator::collect
/// [`Vec`]: std::vec::Vec
#[macro_export]
#[doc(hidden)]
macro_rules! __contains_each {
($(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
(),
$crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Superset)
}};
($($matcher:expr),* $(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
($(
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
$matcher
)
,)*),
$crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Superset)
}}
}
/// Matches a container all of whose elements are matched by the given matchers.
///
/// To match, each element in the container must have a corresponding matcher
/// which matches it. There must be a 1-1 mapping from container elements to
/// matchers, so that no matcher has more than one corresponding element.
///
/// There may, however, be matchers not corresponding to any elements in the
/// container.
///
/// Put another way, `is_contained_in!` matches if there is a subset of the
/// matchers which would match with
/// [`unordered_elements_are`][crate::matchers::unordered_elements_are].
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(vec![2, 1], is_contained_in![eq(&1), ge(&2)])?; // Passes
/// verify_that!(vec![2, 1], is_contained_in![ge(&1), ge(&1)])?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
/// verify_that!(vec![1, 2, 3], is_contained_in![eq(&1), ge(&2)])?; // Fails: container too large
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// verify_that!(vec![2, 1], is_contained_in![eq(&1), ge(&4)])?; // Fails: second matcher unmatched
/// # Ok(())
/// # }
/// # fn should_fail_3() -> Result<()> {
/// verify_that!(vec![3, 1], is_contained_in![ge(&3), ge(&3), ge(&3)])?; // Fails: no matching
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// # should_fail_3().unwrap_err();
/// ```
///
/// The actual value must be a container such as a `&Vec`, an array, or a slice.
/// More precisely, the actual value must implement [`IntoIterator`].
///
/// If an inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
///
/// verify_that!(vec![1,2,3], is_contained_in![lt(&2), &3, &4, gt(&0)])
/// # .unwrap();
/// ```
///
/// The matcher proceeds in three stages:
///
/// 1. It first checks whether the actual value is too large to possibly be
/// matched by each of the given matchers. If so, it immediately fails
/// explaining that the size is too large.
///
/// 2. It then checks whether each actual container element is matched by at
/// least one matcher and fails if that is not the case. The failure message
/// indicates which element had no corresponding matcher.
///
/// 3. Finally, it checks whether the mapping of elements to corresponding
/// matchers is 1-1 and fails if that is not the case. The failure message
/// then shows the best matching it could find, including which container
/// elements did not have corresponding matchers.
///
/// [`IntoIterator`]: std::iter::IntoIterator
/// [`Iterator`]: std::iter::Iterator
/// [`Iterator::collect`]: std::iter::Iterator::collect
/// [`Vec`]: std::vec::Vec
#[macro_export]
#[doc(hidden)]
macro_rules! __is_contained_in {
($(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
(), $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Subset)
}};
($($matcher:expr),* $(,)?) => {{
$crate::matchers::__internal_unstable_do_not_depend_on_these::
UnorderedElementsAreMatcher::new(
($(
$crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
$matcher
)
,)*),
$crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Subset)
}}
}
/// Module for use only by the macros in this module.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
pub mod internal {
use crate::description::Description;
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::match_matrix::internal::{MatchMatrix, Requirements};
use std::fmt::Debug;
/// A trait for a tuple of matchers.
pub trait MatcherTuple<T: Debug + Copy> {
/// Turn the tuple into a vector of matchers.
fn to_matchers(&self) -> Vec<Box<dyn Matcher<T> + '_>>;
}
// Base case, no matchers.
impl<T: Debug + Copy> MatcherTuple<T> for () {
fn to_matchers(&self) -> Vec<Box<dyn Matcher<T> + '_>> {
vec![]
}
}
// A macro for generating a MatcherTuple implementation for a given number
// of matchers.
macro_rules! matcher_tuple_n {
// Given list of (field_number, matcher) pairs, generate a MatcherTuple
// implementation.
($([$field_number:tt, $matcher_type:ident]),*) => {
impl<T: Debug + Copy, $($matcher_type: Matcher<T>),*> MatcherTuple<T>
for ($($matcher_type,)*) {
// Our implementation of to_matchers just converts our tuple into a vector of
// matchers by expanding self.0, self.1, etc.
fn to_matchers(&self) -> Vec<Box<dyn Matcher<T> + '_>> {
vec![$(Box::new(&self.$field_number)),*]
}
}
};
}
matcher_tuple_n!([0, M0]);
matcher_tuple_n!([0, M0], [1, M1]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4], [5, M5]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4], [5, M5], [6, M6]);
matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4], [5, M5], [6, M6], [7, M7]);
matcher_tuple_n!(
[0, M0],
[1, M1],
[2, M2],
[3, M3],
[4, M4],
[5, M5],
[6, M6],
[7, M7],
[8, M8]
);
matcher_tuple_n!(
[0, M0],
[1, M1],
[2, M2],
[3, M3],
[4, M4],
[5, M5],
[6, M6],
[7, M7],
[8, M8],
[9, M9]
);
matcher_tuple_n!(
[0, M0],
[1, M1],
[2, M2],
[3, M3],
[4, M4],
[5, M5],
[6, M6],
[7, M7],
[8, M8],
[9, M9],
[10, M10]
);
matcher_tuple_n!(
[0, M0],
[1, M1],
[2, M2],
[3, M3],
[4, M4],
[5, M5],
[6, M6],
[7, M7],
[8, M8],
[9, M9],
[10, M10],
[11, M11]
);
/// This struct is meant to be used only through the
/// `unordered_elements_are![...]` macro.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
#[derive(MatcherBase)]
pub struct UnorderedElementsAreMatcher<M> {
matchers: M,
requirements: Requirements,
}
impl<M> UnorderedElementsAreMatcher<M> {
pub fn new(matchers: M, requirements: Requirements) -> Self {
Self { matchers, requirements }
}
}
// This matcher performs the checks in three different steps in both `matches`
// and `explain_match`. This is useful for performance but also to produce
// an actionable error message.
// 1. `UnorderedElementsAreMatcher` verifies that both collections have the same
// size
// 2. `UnorderedElementsAreMatcher` verifies that each actual element matches at
// least one expected element and vice versa.
// 3. `UnorderedElementsAreMatcher` verifies that a perfect matching exists
// using Ford-Fulkerson.
impl<T: Debug + Copy, ContainerT: Debug + Copy, M> Matcher<ContainerT>
for UnorderedElementsAreMatcher<M>
where
ContainerT: IntoIterator<Item = T>,
M: MatcherTuple<T>,
{
fn matches(&self, actual: ContainerT) -> MatcherResult {
let matchers = self.matchers.to_matchers();
let match_matrix = MatchMatrix::generate(actual, &matchers);
match_matrix.is_match_for(self.requirements).into()
}
fn explain_match(&self, actual: ContainerT) -> Description {
let matchers = self.matchers.to_matchers();
if let Some(size_mismatch_explanation) =
self.requirements.explain_size_mismatch(actual, matchers.len())
{
return size_mismatch_explanation;
}
let match_matrix = MatchMatrix::generate(actual, &matchers);
if let Some(unmatchable_explanation) =
match_matrix.explain_unmatchable(self.requirements)
{
return unmatchable_explanation;
}
let best_match = match_matrix.find_best_match();
best_match
.get_explanation(actual, &matchers, self.requirements)
.unwrap_or("whose elements all match".into())
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
let matchers = self.matchers.to_matchers();
format!(
"{} elements matching in any order:\n{}",
if matcher_result.into() { "contains" } else { "doesn't contain" },
matchers
.iter()
.map(|matcher| matcher.describe(MatcherResult::Match))
.collect::<Description>()
.enumerate()
.indent()
)
.into()
}
}
}
#[cfg(test)]
mod tests {
use crate as googletest;
use crate::description::Description;
use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
use std::collections::{HashMap, HashSet};
#[test]
fn has_correct_description_for_map() -> googletest::Result<()> {
// UnorderedElementsAreMatcher maintains references to the matchers, so the
// constituent matchers must live longer. Inside a verify_that! macro, the
// compiler takes care of that, but when the matcher is created separately,
// we must create the constitute matchers separately so that they
// aren't dropped too early.
let matchers = ((eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three")));
let matcher = unordered_elements_are![
(matchers.0 .0, matchers.0 .1),
(matchers.1 .0, matchers.1 .1),
(matchers.2 .0, matchers.2 .1)
];
verify_that!(
Matcher::<&HashMap<i32, String>>::describe(&matcher, MatcherResult::Match),
displays_as(eq(indoc!(
"
contains elements matching in any order:
0. is a tuple whose values respectively match:
is equal to 2
is equal to \"Two\"
1. is a tuple whose values respectively match:
is equal to 1
is equal to \"One\"
2. is a tuple whose values respectively match:
is equal to 3
is equal to \"Three\""
)))
)
}
#[test]
fn unordered_elements_are_description_no_full_match_with_map() -> googletest::Result<()> {
// UnorderedElementsAreMatcher maintains references to the matchers, so the
// constituent matchers must live longer. Inside a verify_that! macro, the
// compiler takes care of that, but when the matcher is created separately,
// we must create the constitute matchers separately so that they
// aren't dropped too early.
let value: HashMap<u32, u32> = HashMap::from_iter([(0, 1), (1, 1), (2, 2)]);
let matchers = ((anything(), eq(&1)), (anything(), eq(&2)), (anything(), eq(&2)));
let matcher = unordered_elements_are![
(matchers.0 .0, matchers.0 .1),
(matchers.1 .0, matchers.1 .1),
(matchers.2 .0, matchers.2 .1),
];
verify_that!(
matcher.explain_match(&value),
all![
displays_as(contains_regex(
"Actual element \\(2, 2\\) at index [0-2] matched expected element `is a tuple whose values respectively match:\n is anything\n is equal to 2` at index [0-2]."
)),
displays_as(contains_regex(
"Actual element \\(\n [0-1],\n [0-1],\n \\) at index [0-2] did not match any remaining expected element."
)),
displays_as(contains_substring(
"Expected element `is a tuple whose values respectively match:\n is anything\n is equal to 2` at index 2 did not match any remaining actual element."
))
]
)
}
// This test verifies that unordered_elements_are can be used as the inner
// matcher of another matcher, where the inner matcher is given a reference
// to a local variable. If unordered_elements_are stores its matchers in a
// Vec with the same type and lifetimes then this fails to compile.
#[test]
fn works_with_inner_matcher_that_modifies_the_input() -> googletest::Result<()> {
// A custom matcher that applies an operation to the input hashmap (filtering
// out any keys present in `to_filter`) and then applies the inner
// matcher to the modified input.
fn matches_filtered<'a, M>(
to_filter: &'a HashSet<i32>,
inner: M,
) -> impl Matcher<&'a HashSet<i32>>
where
M: for<'b> Matcher<&'b HashSet<i32>>,
{
#[derive(MatcherBase)]
struct FilteredMatcher<'a, M> {
to_filter: &'a HashSet<i32>,
inner: M,
}
impl<'a, M> Matcher<&'a HashSet<i32>> for FilteredMatcher<'a, M>
where
M: for<'b> Matcher<&'b HashSet<i32>>,
{
fn matches(&self, actual: &'a HashSet<i32>) -> MatcherResult {
let filtered: HashSet<i32> =
actual.iter().filter(|k| !self.to_filter.contains(k)).cloned().collect();
self.inner.matches(&filtered)
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
self.inner.describe(matcher_result)
}
fn explain_match(&self, actual: &'a HashSet<i32>) -> Description {
let filtered: HashSet<i32> =
actual.iter().filter(|k| !self.to_filter.contains(k)).cloned().collect();
self.inner.explain_match(&filtered)
}
}
FilteredMatcher { to_filter, inner }
}
let before: HashSet<i32> = HashSet::from([1]);
let after: HashSet<i32> = HashSet::from([1, 2]);
verify_that!(&after, matches_filtered(&before, unordered_elements_are![eq(&2)]))?;
Ok(())
}
}