• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, MatcherBase, MatcherResult},
18 };
19 use std::fmt::Debug;
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<Inner>(inner: Inner) -> SomeMatcher<Inner>41 pub fn some<Inner>(inner: Inner) -> SomeMatcher<Inner> {
42     SomeMatcher { inner }
43 }
44 
45 #[derive(MatcherBase)]
46 pub struct SomeMatcher<InnerMatcherT> {
47     inner: InnerMatcherT,
48 }
49 
50 impl<T: Debug + Copy, InnerMatcherT: Matcher<T>> Matcher<Option<T>> for SomeMatcher<InnerMatcherT> {
matches(&self, actual: Option<T>) -> MatcherResult51     fn matches(&self, actual: Option<T>) -> MatcherResult {
52         actual.map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
53     }
54 
explain_match(&self, actual: Option<T>) -> Description55     fn explain_match(&self, actual: Option<T>) -> Description {
56         match (self.matches(actual), actual) {
57             (_, Some(t)) => {
58                 Description::new().text("which has a value").nested(self.inner.explain_match(t))
59             }
60             (_, None) => "which is None".into(),
61         }
62     }
63 
describe(&self, matcher_result: MatcherResult) -> Description64     fn describe(&self, matcher_result: MatcherResult) -> Description {
65         match matcher_result {
66             MatcherResult::Match => {
67                 format!("has a value which {}", self.inner.describe(MatcherResult::Match)).into()
68             }
69             MatcherResult::NoMatch => format!(
70                 "is None or has a value which {}",
71                 self.inner.describe(MatcherResult::NoMatch)
72             )
73             .into(),
74         }
75     }
76 }
77 
78 impl<'a, T: Debug, InnerMatcherT: Matcher<&'a T>> Matcher<&'a Option<T>>
79     for SomeMatcher<InnerMatcherT>
80 {
matches(&self, actual: &'a Option<T>) -> MatcherResult81     fn matches(&self, actual: &'a Option<T>) -> MatcherResult {
82         actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
83     }
84 
explain_match(&self, actual: &'a Option<T>) -> Description85     fn explain_match(&self, actual: &'a Option<T>) -> Description {
86         match (self.matches(actual), actual) {
87             (_, Some(t)) => {
88                 Description::new().text("which has a value").nested(self.inner.explain_match(t))
89             }
90             (_, None) => "which is None".into(),
91         }
92     }
93 
describe(&self, matcher_result: MatcherResult) -> Description94     fn describe(&self, matcher_result: MatcherResult) -> Description {
95         match matcher_result {
96             MatcherResult::Match => {
97                 format!("has a value which {}", self.inner.describe(MatcherResult::Match)).into()
98             }
99             MatcherResult::NoMatch => format!(
100                 "is None or has a value which {}",
101                 self.inner.describe(MatcherResult::NoMatch)
102             )
103             .into(),
104         }
105     }
106 }
107 
108 #[cfg(test)]
109 mod tests {
110     use crate::matcher::MatcherResult;
111     use crate::prelude::*;
112     use indoc::indoc;
113 
114     #[test]
some_matches_option_with_value() -> Result<()>115     fn some_matches_option_with_value() -> Result<()> {
116         let matcher = some(eq(1));
117 
118         let result = matcher.matches(Some(1));
119 
120         verify_that!(result, eq(MatcherResult::Match))
121     }
122 
123     #[test]
some_does_not_match_option_with_wrong_value() -> Result<()>124     fn some_does_not_match_option_with_wrong_value() -> Result<()> {
125         let matcher = some(eq(1));
126 
127         let result = matcher.matches(Some(0));
128 
129         verify_that!(result, eq(MatcherResult::NoMatch))
130     }
131 
132     #[test]
some_does_not_match_option_with_none() -> Result<()>133     fn some_does_not_match_option_with_none() -> Result<()> {
134         let matcher = some(eq(1));
135 
136         let result = matcher.matches(None::<i32>);
137 
138         verify_that!(result, eq(MatcherResult::NoMatch))
139     }
140 
141     #[test]
some_matches_option_with_by_ref_value() -> Result<()>142     fn some_matches_option_with_by_ref_value() -> Result<()> {
143         verify_that!(Some("123".to_string()), some(eq("123")))
144     }
145 
146     #[test]
some_does_not_match_option_with_wrong_by_ref_value() -> Result<()>147     fn some_does_not_match_option_with_wrong_by_ref_value() -> Result<()> {
148         verify_that!(Some("321".to_string()), not(some(eq("123"))))
149     }
150 
151     #[test]
some_does_not_match_option_with_by_ref_none() -> Result<()>152     fn some_does_not_match_option_with_by_ref_none() -> Result<()> {
153         verify_that!(None::<String>, not(some(eq("123"))))
154     }
155 
156     #[test]
some_full_error_message() -> Result<()>157     fn some_full_error_message() -> Result<()> {
158         let result = verify_that!(Some(2), some(eq(1)));
159         verify_that!(
160             result,
161             err(displays_as(contains_substring(indoc!(
162                 "
163                     Value of: Some(2)
164                     Expected: has a value which is equal to 1
165                     Actual: Some(2),
166                       which has a value
167                         which isn't equal to 1
168                 "
169             ))))
170         )
171     }
172 
173     #[test]
some_describe_matches() -> Result<()>174     fn some_describe_matches() -> Result<()> {
175         verify_that!(
176             Matcher::<Option<i32>>::describe(&some(eq(1)), MatcherResult::Match),
177             displays_as(eq("has a value which is equal to 1"))
178         )
179     }
180 
181     #[test]
some_describe_does_not_match() -> Result<()>182     fn some_describe_does_not_match() -> Result<()> {
183         verify_that!(
184             Matcher::<Option<i32>>::describe(&some(eq(1)), MatcherResult::NoMatch),
185             displays_as(eq("is None or has a value which isn't equal to 1"))
186         )
187     }
188 
189     #[test]
some_describe_matches_of_by_ref() -> Result<()>190     fn some_describe_matches_of_by_ref() -> Result<()> {
191         verify_that!(
192             Matcher::<Option<&String>>::describe(&some(eq("123")), MatcherResult::Match),
193             displays_as(eq("has a value which is equal to \"123\""))
194         )
195     }
196 
197     #[test]
some_describe_does_not_match_of_by_ref() -> Result<()>198     fn some_describe_does_not_match_of_by_ref() -> Result<()> {
199         verify_that!(
200             Matcher::<Option<&String>>::describe(&some(eq("123")), MatcherResult::NoMatch),
201             displays_as(eq("is None or has a value which isn't equal to \"123\""))
202         )
203     }
204 
205     #[test]
some_explain_match_with_none() -> Result<()>206     fn some_explain_match_with_none() -> Result<()> {
207         verify_that!(some(eq(1)).explain_match(None::<i32>), displays_as(eq("which is None")))
208     }
209 
210     #[test]
some_explain_match_with_some_success() -> Result<()>211     fn some_explain_match_with_some_success() -> Result<()> {
212         verify_that!(
213             some(eq(1)).explain_match(Some(1)),
214             displays_as(eq("which has a value\n  which is equal to 1"))
215         )
216     }
217 
218     #[test]
some_explain_match_with_some_fail() -> Result<()>219     fn some_explain_match_with_some_fail() -> Result<()> {
220         verify_that!(
221             some(eq(1)).explain_match(Some(2)),
222             displays_as(eq("which has a value\n  which isn't equal to 1"))
223         )
224     }
225 
226     #[test]
some_explain_match_with_none_by_ref() -> Result<()>227     fn some_explain_match_with_none_by_ref() -> Result<()> {
228         verify_that!(
229             some(eq("123")).explain_match(&None::<String>),
230             displays_as(eq("which is None"))
231         )
232     }
233 
234     #[test]
some_explain_match_with_some_success_by_ref() -> Result<()>235     fn some_explain_match_with_some_success_by_ref() -> Result<()> {
236         verify_that!(
237             some(eq("123")).explain_match(&Some("123".to_string())),
238             displays_as(eq("which has a value\n  which is equal to \"123\""))
239         )
240     }
241 
242     #[test]
some_explain_match_with_some_fail_by_ref() -> Result<()>243     fn some_explain_match_with_some_fail_by_ref() -> Result<()> {
244         verify_that!(
245             some(eq("123")).explain_match(&Some("321".to_string())),
246             displays_as(eq("which has a value\n  which isn't equal to \"123\""))
247         )
248     }
249 }
250