1 // Copyright 2023 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 // There are no visible documentation elements in this module; the declarative 16 // macro is documented in the matcher module. 17 #![doc(hidden)] 18 19 /// Matches an object which, upon calling the given method on it with the given 20 /// arguments, produces a value matched by the given inner matcher. 21 /// 22 /// This is particularly useful as a nested matcher when the desired 23 /// property cannot be accessed through a field and must instead be 24 /// extracted through a method call. For example: 25 /// 26 /// ``` 27 /// # use googletest::prelude::*; 28 /// #[derive(Debug)] 29 /// pub struct MyStruct { 30 /// a_field: u32, 31 /// } 32 /// 33 /// impl MyStruct { 34 /// pub fn get_a_field(&self) -> u32 { self.a_field } 35 /// } 36 /// 37 /// let value = vec![MyStruct { a_field: 100 }]; 38 /// verify_that!(value, contains(property!(MyStruct.get_a_field(), eq(100)))) 39 /// # .unwrap(); 40 /// ``` 41 /// 42 /// **Important**: The method should be pure function with a deterministic 43 /// output and no side effects. In particular, in the event of an assertion 44 /// failure, it will be invoked a second time, with the assertion failure output 45 /// reflecting the *second* invocation. 46 /// 47 /// If the method returns a *reference*, then it must be preceded by a `*`: 48 /// 49 /// ``` 50 /// # use googletest::prelude::*; 51 /// # #[derive(Debug)] 52 /// # pub struct MyStruct { 53 /// # a_field: u32, 54 /// # } 55 /// impl MyStruct { 56 /// pub fn get_a_field(&self) -> &u32 { &self.a_field } 57 /// } 58 /// 59 /// # let value = vec![MyStruct { a_field: 100 }]; 60 /// verify_that!(value, contains(property!(*MyStruct.get_a_field(), eq(100)))) 61 /// # .unwrap(); 62 /// ``` 63 /// 64 /// The method may also take additional arguments: 65 /// 66 /// ``` 67 /// # use googletest::prelude::*; 68 /// # #[derive(Debug)] 69 /// # pub struct MyStruct { 70 /// # a_field: u32, 71 /// # } 72 /// impl MyStruct { 73 /// pub fn add_to_a_field(&self, a: u32) -> u32 { self.a_field + a } 74 /// } 75 /// 76 /// # let value = vec![MyStruct { a_field: 100 }]; 77 /// verify_that!(value, contains(property!(MyStruct.add_to_a_field(50), eq(150)))) 78 /// # .unwrap(); 79 /// ``` 80 /// 81 /// Unfortunately, this matcher does *not* work with methods returning string 82 /// slices: 83 /// 84 /// ```compile_fail 85 /// # use googletest::prelude::*; 86 /// #[derive(Debug)] 87 /// pub struct MyStruct { 88 /// a_string: String, 89 /// } 90 /// impl MyStruct { 91 /// pub fn get_a_string(&self) -> &str { &self.a_string } 92 /// } 93 /// 94 /// let value = MyStruct { a_string: "A string".into() }; 95 /// verify_that!(value, property!(*MyStruct.get_a_string(), eq("A string"))) // Does not compile 96 /// # .unwrap(); 97 /// ``` 98 /// 99 /// This macro is analogous to [`field`][crate::matchers::field], except that it 100 /// extracts the datum to be matched from the given object by invoking a method 101 /// rather than accessing a field. 102 /// 103 /// The list of arguments may optionally have a trailing comma. 104 #[macro_export] 105 #[doc(hidden)] 106 macro_rules! __property { 107 ($($t:tt)*) => { $crate::property_internal!($($t)*) } 108 } 109 110 // Internal-only macro created so that the macro definition does not appear in 111 // generated documentation. 112 #[doc(hidden)] 113 #[macro_export] 114 macro_rules! property_internal { 115 ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{ 116 use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher; 117 property_matcher( 118 |o: &$($t)::+| o.$method($($argument),*), 119 &stringify!($method($($argument),*)), 120 $m) 121 }}; 122 123 (* $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{ 124 use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher; 125 property_ref_matcher( 126 |o: &$($t)::+| o.$method($($argument),*), 127 &stringify!($method($($argument),*)), 128 $m) 129 }}; 130 } 131 132 /// Items for use only by the declarative macros in this module. 133 /// 134 /// **For internal use only. API stablility is not guaranteed!** 135 #[doc(hidden)] 136 pub mod internal { 137 use crate::{ 138 description::Description, 139 matcher::{Matcher, MatcherResult}, 140 }; 141 use std::{fmt::Debug, marker::PhantomData}; 142 143 /// **For internal use only. API stablility is not guaranteed!** 144 #[doc(hidden)] property_matcher<OuterT: Debug, InnerT: Debug, MatcherT: Matcher<ActualT = InnerT>>( extractor: impl Fn(&OuterT) -> InnerT, property_desc: &'static str, inner: MatcherT, ) -> impl Matcher<ActualT = OuterT>145 pub fn property_matcher<OuterT: Debug, InnerT: Debug, MatcherT: Matcher<ActualT = InnerT>>( 146 extractor: impl Fn(&OuterT) -> InnerT, 147 property_desc: &'static str, 148 inner: MatcherT, 149 ) -> impl Matcher<ActualT = OuterT> { 150 PropertyMatcher { extractor, property_desc, inner, phantom: Default::default() } 151 } 152 153 struct PropertyMatcher<OuterT, ExtractorT, MatcherT> { 154 extractor: ExtractorT, 155 property_desc: &'static str, 156 inner: MatcherT, 157 phantom: PhantomData<OuterT>, 158 } 159 160 impl<InnerT, OuterT, ExtractorT, MatcherT> Matcher for PropertyMatcher<OuterT, ExtractorT, MatcherT> 161 where 162 InnerT: Debug, 163 OuterT: Debug, 164 ExtractorT: Fn(&OuterT) -> InnerT, 165 MatcherT: Matcher<ActualT = InnerT>, 166 { 167 type ActualT = OuterT; 168 matches(&self, actual: &OuterT) -> MatcherResult169 fn matches(&self, actual: &OuterT) -> MatcherResult { 170 self.inner.matches(&(self.extractor)(actual)) 171 } 172 describe(&self, matcher_result: MatcherResult) -> Description173 fn describe(&self, matcher_result: MatcherResult) -> Description { 174 format!( 175 "has property `{}`, which {}", 176 self.property_desc, 177 self.inner.describe(matcher_result) 178 ) 179 .into() 180 } 181 explain_match(&self, actual: &OuterT) -> Description182 fn explain_match(&self, actual: &OuterT) -> Description { 183 let actual_inner = (self.extractor)(actual); 184 format!( 185 "whose property `{}` is `{:#?}`, {}", 186 self.property_desc, 187 actual_inner, 188 self.inner.explain_match(&actual_inner) 189 ) 190 .into() 191 } 192 } 193 194 /// **For internal use only. API stablility is not guaranteed!** 195 #[doc(hidden)] property_ref_matcher<OuterT, InnerT, MatcherT>( extractor: fn(&OuterT) -> &InnerT, property_desc: &'static str, inner: MatcherT, ) -> impl Matcher<ActualT = OuterT> where OuterT: Debug, InnerT: Debug + ?Sized, MatcherT: Matcher<ActualT = InnerT>,196 pub fn property_ref_matcher<OuterT, InnerT, MatcherT>( 197 extractor: fn(&OuterT) -> &InnerT, 198 property_desc: &'static str, 199 inner: MatcherT, 200 ) -> impl Matcher<ActualT = OuterT> 201 where 202 OuterT: Debug, 203 InnerT: Debug + ?Sized, 204 MatcherT: Matcher<ActualT = InnerT>, 205 { 206 PropertyRefMatcher { extractor, property_desc, inner } 207 } 208 209 struct PropertyRefMatcher<InnerT: ?Sized, OuterT, MatcherT> { 210 extractor: fn(&OuterT) -> &InnerT, 211 property_desc: &'static str, 212 inner: MatcherT, 213 } 214 215 impl<InnerT: Debug + ?Sized, OuterT: Debug, MatcherT: Matcher<ActualT = InnerT>> Matcher 216 for PropertyRefMatcher<InnerT, OuterT, MatcherT> 217 { 218 type ActualT = OuterT; 219 matches(&self, actual: &OuterT) -> MatcherResult220 fn matches(&self, actual: &OuterT) -> MatcherResult { 221 self.inner.matches((self.extractor)(actual)) 222 } 223 describe(&self, matcher_result: MatcherResult) -> Description224 fn describe(&self, matcher_result: MatcherResult) -> Description { 225 format!( 226 "has property `{}`, which {}", 227 self.property_desc, 228 self.inner.describe(matcher_result) 229 ) 230 .into() 231 } 232 explain_match(&self, actual: &OuterT) -> Description233 fn explain_match(&self, actual: &OuterT) -> Description { 234 let actual_inner = (self.extractor)(actual); 235 format!( 236 "whose property `{}` is `{:#?}`, {}", 237 self.property_desc, 238 actual_inner, 239 self.inner.explain_match(actual_inner) 240 ) 241 .into() 242 } 243 } 244 } 245