// 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. //! The components required to implement matchers. use crate::description::Description; use crate::internal::test_outcome::TestAssertionFailure; use crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher; use crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher; pub use googletest_macro::MatcherBase; use std::fmt::Debug; /// An interface for checking an arbitrary condition on a datum. /// /// This trait is automatically implemented for a reference of any type /// implementing `Matcher`. This simplifies reusing a matcher in different /// assertions. /// /// It is also implemented for tuple of `Matcher`. If `MatcherT: Matcher` and /// `MatcherU: Matcher`, then `(MatcherT, MatcherU): Matcher<(T, U)>`, and so /// on, up to 12 elements. Tuples longer than that do not automatically inherit /// the `Debug` trait from their members, so are generally not well-supported; /// see [Rust by Example](https://doc.rust-lang.org/rust-by-example/primitives/tuples.html#tuples). // `ActualT` requires `Copy` so that `actual` could be passed to `matches` and // if it fails passed to `explain_match`. We can relax this constraint later by // requiring only `Clone`. pub trait Matcher: MatcherBase { /// Returns whether the condition matches the datum `actual`. /// /// The trait implementation defines what it means to "match". Often the /// matching condition is based on data stored in the matcher. For example, /// `eq` matches when its stored expected value is equal (in the sense of /// the `==` operator) to the value `actual`. fn matches(&self, actual: ActualT) -> MatcherResult; /// Returns a description of `self` according to the supplied matching /// sense: a positive description if `sense` is `Match`, and a negative /// description if it's `NoMatch`. /// /// The function should print a verb phrase that describes the property a /// value matching, respectively not matching, this matcher should have. /// The subject of the verb phrase is the value being matched. /// /// The output appears next to `Expected` in an assertion failure message. /// For example: /// /// ```text /// Value of: ... /// Expected: is equal to 7 /// ^^^^^^^^^^^^^ /// Actual: ... /// ``` /// /// When the matcher contains one or more inner matchers, the implementation /// should invoke [`Self::describe`] on the inner matchers to complete the /// description. It should place the inner description at a point where a /// verb phrase would fit. For example, the matcher /// [`some`][crate::matchers::some] implements `describe` as follows: /// /// ```ignore /// fn describe(&self, sense: MatcherResult) -> Description { /// match sense { /// MatcherResult::Match => { /// Description::new() /// .text("has a value which") /// .nested(self.inner.describe(MatcherResult::Match)) /// // Inner matcher: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// } /// MatcherResult::NoMatch => { /// Description::new() /// .text("is None or has a value which") /// .nested(self.inner.describe(MatcherResult::NoMatch)) /// } /// } /// } /// ``` /// /// The output expectation differs from that of /// [`explain_match`][Self::explain_match] in that it is a verb phrase /// (beginning with a verb like "is") rather than a relative clause /// (beginning with "which" or "whose"). This difference is because the /// output of `explain_match` is always used adjectivally to describe the /// actual value, while `describe` is used in contexts where a relative /// clause would not make sense. fn describe(&self, sense: MatcherResult) -> Description; /// Prepares a [`String`] describing how the expected value /// encoded in this instance matches or does not match the given value /// `actual`. /// /// This should be in the form of a relative clause, i.e. something starting /// with a relative pronoun such as "which" or "whose". It will appear next /// to the actual value in an assertion failure. For example: /// /// ```text /// Value of: ... /// Expected: ... /// Actual: ["Something"], which does not contain "Something else" /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// ``` /// /// The default implementation relies on [`describe`][Self::describe]. Thus /// it does not make any use of the actual value itself, but rather only /// whether the value is matched. /// /// Override the default implementation to provide additional context on why /// a particular value matched or did not match. For example, the /// [`container_eq`][crate::matchers::container_eq] matcher displays /// information on which elements of the actual value were not present in /// the expected value and vice versa. /// /// This implementation should be overridden in any matcher which contains /// one or more inner matchers. The implementation should invoke /// `explain_match` on the inner matchers, so that the generated match /// explanation also reflects their implementation. Without this, the match /// explanation of the inner matchers will not be able to make use of the /// actual value at all. /// /// For example, the `explain_match` implementation of the matcher /// [`points_to`][crate::matchers::points_to] defers immediately to the /// inner matcher and appears as follows: /// /// ```ignore /// fn explain_match(&self, actual: &ActualT) -> Description { /// self.expected.explain_match(*actual) /// } /// ``` /// /// The matcher can also provide some additional context before deferring to /// an inner matcher. In that case it should invoke `explain_match` on the /// inner matcher at a point where a relative clause would fit. For example: /// /// ```ignore /// fn explain_match(&self, actual: ActualT) -> Description { /// Description::new() /// .text("which points to a value") /// .nested(self.expected.explain_match(*actual)) /// } /// ``` fn explain_match(&self, actual: ActualT) -> Description { format!("which {}", self.describe(self.matches(actual))).into() } } /// Base trait for matchers. Any type implementing `Matcher` must implement /// `MatcherBase`, but that should be done through the `#[derive(MatcherBase)]` /// macro. // The `and` and `or` functions cannot be part of the `Matcher` trait since it // can be implemented multiple times for a given matcher type. Consider that // `and` and `or` are part of the `Matcher` trait and `MyMatcher` implements // both `Matcher` and `Matcher`. Then `MyMatcher{...}.and(...)` can be // either: // * `Matcher::::and(MyMatcher{...}, ...)` or // * `Matcher::::and(MyMatcher{...}, ...)`. // Moving the `and` and `or` functions in a non-generic trait removes this // confusion by making `and` and `or` unique for a given type. pub trait MatcherBase { /// Constructs a matcher that matches both `self` and `right`. /// /// ``` /// # use googletest::prelude::*; /// # fn should_pass() -> Result<()> { /// verify_that!("A string", starts_with("A").and(ends_with("string")))?; // Passes /// # Ok(()) /// # } /// # fn should_fail_1() -> Result<()> { /// verify_that!("A string", starts_with("Another").and(ends_with("string")))?; // Fails /// # Ok(()) /// # } /// # fn should_fail_2() -> Result<()> { /// verify_that!("A string", starts_with("A").and(ends_with("non-string")))?; // Fails /// # Ok(()) /// # } /// # should_pass().unwrap(); /// # should_fail_1().unwrap_err(); /// # should_fail_2().unwrap_err(); /// ``` // TODO(b/264518763): Replace the return type with impl Matcher and reduce // visibility of ConjunctionMatcher once impl in return position in trait // methods is stable. fn and(self, right: Right) -> ConjunctionMatcher where Self: Sized, { ConjunctionMatcher::new(self, right) } /// Constructs a matcher that matches when at least one of `self` or `right` /// matches the input. /// /// ``` /// # use googletest::prelude::*; /// # fn should_pass() -> Result<()> { /// verify_that!(10, eq(2).or(ge(5)))?; // Passes /// verify_that!(10, eq(2).or(eq(5)).or(ge(9)))?; // Passes /// # Ok(()) /// # } /// # fn should_fail() -> Result<()> { /// verify_that!(10, eq(2).or(ge(15)))?; // Fails /// # Ok(()) /// # } /// # should_pass().unwrap(); /// # should_fail().unwrap_err(); /// ``` // TODO(b/264518763): Replace the return type with impl Matcher and reduce // visibility of DisjunctionMatcher once impl in return position in trait // methods is stable. fn or(self, right: Right) -> DisjunctionMatcher where Self: Sized, { DisjunctionMatcher::new(self, right) } } /// Any actual value whose debug length is greater than this value will be /// pretty-printed. Otherwise, it will have normal debug output formatting. const PRETTY_PRINT_LENGTH_THRESHOLD: usize = 60; /// Constructs a [`TestAssertionFailure`] reporting that the given `matcher` /// does not match the value `actual`. /// /// The parameter `actual_expr` contains the expression which was evaluated to /// obtain `actual`. #[track_caller] pub(crate) fn create_assertion_failure( matcher: &impl Matcher, actual: T, actual_expr: &'static str, ) -> TestAssertionFailure { let actual_formatted = format!("{actual:?}"); let actual_formatted = if actual_formatted.len() > PRETTY_PRINT_LENGTH_THRESHOLD { format!("{actual:#?}") } else { actual_formatted }; TestAssertionFailure::create(format!( "\ Value of: {actual_expr} Expected: {} Actual: {actual_formatted}, {}", matcher.describe(MatcherResult::Match), matcher.explain_match(actual).indent(), )) } /// The result of applying a [`Matcher`] on an actual value. #[derive(Debug, PartialEq, Clone, Copy)] pub enum MatcherResult { /// The actual value matches according to the [`Matcher`] definition. Match, /// The actual value does not match according to the [`Matcher`] definition. NoMatch, } impl From for MatcherResult { fn from(b: bool) -> Self { if b { MatcherResult::Match } else { MatcherResult::NoMatch } } } impl From for bool { fn from(matcher_result: MatcherResult) -> Self { matcher_result.is_match() } } impl MatcherResult { /// Returns `true` if `self` is [`MatcherResult::Match`], otherwise /// `false`. pub fn is_match(self) -> bool { matches!(self, MatcherResult::Match) } /// Returns `true` if `self` is [`MatcherResult::NoMatch`], otherwise /// `false`. pub fn is_no_match(self) -> bool { matches!(self, MatcherResult::NoMatch) } } impl MatcherBase for &M {} impl> Matcher for &M { fn matches(&self, actual: T) -> MatcherResult { (*self).matches(actual) } fn describe(&self, matcher_result: MatcherResult) -> Description { (*self).describe(matcher_result) } fn explain_match(&self, actual: T) -> Description { (*self).explain_match(actual) } } #[cfg(test)] mod tests { use crate::prelude::*; use crate::Result; #[test] fn ref_matchers_can_be_reused() -> Result<()> { let matcher = eq(1); verify_that!(1, &matcher)?; verify_that!(1, &matcher) } #[test] fn ref_matchers_as_inner_matcher() -> Result<()> { let matcher = gt(1); verify_that!([2, 3, 4, 5], [&matcher, &matcher, &matcher, &matcher]) } }