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