1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use crate::{
16 description::Description,
17 matcher::{Matcher, MatcherResult},
18 };
19 use std::{fmt::Debug, marker::PhantomData};
20
21 /// Matches an `Option` containing a value matched by `inner`.
22 ///
23 /// ```
24 /// # use googletest::prelude::*;
25 /// # fn should_pass() -> Result<()> {
26 /// verify_that!(Some("Some value"), some(eq("Some value")))?; // Passes
27 /// # Ok(())
28 /// # }
29 /// # fn should_fail_1() -> Result<()> {
30 /// verify_that!(None::<&str>, some(eq("Some value")))?; // Fails
31 /// # Ok(())
32 /// # }
33 /// # fn should_fail_2() -> Result<()> {
34 /// verify_that!(Some("Some value"), some(eq("Some other value")))?; // Fails
35 /// # Ok(())
36 /// # }
37 /// # should_pass().unwrap();
38 /// # should_fail_1().unwrap_err();
39 /// # should_fail_2().unwrap_err();
40 /// ```
some<T: Debug>(inner: impl Matcher<ActualT = T>) -> impl Matcher<ActualT = Option<T>>41 pub fn some<T: Debug>(inner: impl Matcher<ActualT = T>) -> impl Matcher<ActualT = Option<T>> {
42 SomeMatcher { inner, phantom: Default::default() }
43 }
44
45 struct SomeMatcher<T, InnerMatcherT> {
46 inner: InnerMatcherT,
47 phantom: PhantomData<T>,
48 }
49
50 impl<T: Debug, InnerMatcherT: Matcher<ActualT = T>> Matcher for SomeMatcher<T, InnerMatcherT> {
51 type ActualT = Option<T>;
52
matches(&self, actual: &Option<T>) -> MatcherResult53 fn matches(&self, actual: &Option<T>) -> MatcherResult {
54 actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
55 }
56
explain_match(&self, actual: &Option<T>) -> Description57 fn explain_match(&self, actual: &Option<T>) -> Description {
58 match (self.matches(actual), actual) {
59 (_, Some(t)) => {
60 Description::new().text("which has a value").nested(self.inner.explain_match(t))
61 }
62 (_, None) => "which is None".into(),
63 }
64 }
65
describe(&self, matcher_result: MatcherResult) -> Description66 fn describe(&self, matcher_result: MatcherResult) -> Description {
67 match matcher_result {
68 MatcherResult::Match => {
69 format!("has a value which {}", self.inner.describe(MatcherResult::Match)).into()
70 }
71 MatcherResult::NoMatch => format!(
72 "is None or has a value which {}",
73 self.inner.describe(MatcherResult::NoMatch)
74 )
75 .into(),
76 }
77 }
78 }
79
80 #[cfg(test)]
81 mod tests {
82 use super::some;
83 use crate::matcher::{Matcher, MatcherResult};
84 use crate::prelude::*;
85 use indoc::indoc;
86
87 #[test]
some_matches_option_with_value() -> Result<()>88 fn some_matches_option_with_value() -> Result<()> {
89 let matcher = some(eq(1));
90
91 let result = matcher.matches(&Some(1));
92
93 verify_that!(result, eq(MatcherResult::Match))
94 }
95
96 #[test]
some_does_not_match_option_with_wrong_value() -> Result<()>97 fn some_does_not_match_option_with_wrong_value() -> Result<()> {
98 let matcher = some(eq(1));
99
100 let result = matcher.matches(&Some(0));
101
102 verify_that!(result, eq(MatcherResult::NoMatch))
103 }
104
105 #[test]
some_does_not_match_option_with_none() -> Result<()>106 fn some_does_not_match_option_with_none() -> Result<()> {
107 let matcher = some(eq::<i32, _>(1));
108
109 let result = matcher.matches(&None);
110
111 verify_that!(result, eq(MatcherResult::NoMatch))
112 }
113
114 #[test]
some_full_error_message() -> Result<()>115 fn some_full_error_message() -> Result<()> {
116 let result = verify_that!(Some(2), some(eq(1)));
117 verify_that!(
118 result,
119 err(displays_as(contains_substring(indoc!(
120 "
121 Value of: Some(2)
122 Expected: has a value which is equal to 1
123 Actual: Some(2),
124 which has a value
125 which isn't equal to 1
126 "
127 ))))
128 )
129 }
130
131 #[test]
some_describe_matches() -> Result<()>132 fn some_describe_matches() -> Result<()> {
133 verify_that!(
134 some(eq::<i32, _>(1)).describe(MatcherResult::Match),
135 displays_as(eq("has a value which is equal to 1"))
136 )
137 }
138
139 #[test]
some_describe_does_not_match() -> Result<()>140 fn some_describe_does_not_match() -> Result<()> {
141 verify_that!(
142 some(eq::<i32, _>(1)).describe(MatcherResult::NoMatch),
143 displays_as(eq("is None or has a value which isn't equal to 1"))
144 )
145 }
146
147 #[test]
some_explain_match_with_none() -> Result<()>148 fn some_explain_match_with_none() -> Result<()> {
149 verify_that!(some(eq::<i32, _>(1)).explain_match(&None), displays_as(eq("which is None")))
150 }
151
152 #[test]
some_explain_match_with_some_success() -> Result<()>153 fn some_explain_match_with_some_success() -> Result<()> {
154 verify_that!(
155 some(eq(1)).explain_match(&Some(1)),
156 displays_as(eq("which has a value\n which is equal to 1"))
157 )
158 }
159
160 #[test]
some_explain_match_with_some_fail() -> Result<()>161 fn some_explain_match_with_some_fail() -> Result<()> {
162 verify_that!(
163 some(eq(1)).explain_match(&Some(2)),
164 displays_as(eq("which has a value\n which isn't equal to 1"))
165 )
166 }
167 }
168