Skip to content

Commit b1f77ca

Browse files
Merge pull request google#401 from google:auto_ref_eq
PiperOrigin-RevId: 639029045
2 parents d81d6c9 + 6648c70 commit b1f77ca

File tree

8 files changed

+222
-7
lines changed

8 files changed

+222
-7
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2024 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+
#![doc(hidden)]
16+
17+
/// Auto-ref macro that wrap the expression with `eq(...)` if the expression is
18+
/// not a matcher.
19+
///
20+
/// This is useful to let users pass expected value to macro matchers like
21+
/// `field!` and `property!`.
22+
///`
23+
/// **For internal use only. API stablility is not guaranteed!**
24+
/// If you are interested in using it in your matcher, please file an issue to
25+
/// stabilize this.
26+
#[macro_export]
27+
macro_rules! __auto_ref_eq {
28+
($e:expr) => {{
29+
#[allow(unused_imports)]
30+
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::{
31+
ExpectedKind, MatcherKind,
32+
};
33+
match $e {
34+
expected => (&expected).kind().new(expected),
35+
}
36+
}};
37+
}
38+
39+
// This reimplements the pattern presented in
40+
// https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md
41+
pub mod internal {
42+
use crate::{
43+
matcher::MatcherBase,
44+
matchers::{eq, EqMatcher},
45+
};
46+
47+
pub struct MatcherTag;
48+
49+
pub trait MatcherKind {
50+
#[inline]
51+
fn kind(&self) -> MatcherTag {
52+
MatcherTag
53+
}
54+
}
55+
56+
impl<M: MatcherBase> MatcherKind for M {}
57+
58+
impl MatcherTag {
59+
#[inline]
60+
pub fn new<M>(self, matcher: M) -> M {
61+
matcher
62+
}
63+
}
64+
65+
pub struct ExpectedTag;
66+
67+
pub trait ExpectedKind {
68+
#[inline]
69+
fn kind(&self) -> ExpectedTag {
70+
ExpectedTag
71+
}
72+
}
73+
74+
impl<T> ExpectedKind for &T {}
75+
76+
impl ExpectedTag {
77+
#[inline]
78+
pub fn new<T>(self, expected: T) -> EqMatcher<T> {
79+
eq(expected)
80+
}
81+
}
82+
}
83+
84+
#[cfg(test)]
85+
mod tests {
86+
use crate::prelude::*;
87+
88+
#[test]
89+
fn auto_ref_matcher() -> Result<()> {
90+
verify_that!(123, __auto_ref_eq!(ge(9)))
91+
}
92+
93+
#[test]
94+
fn auto_ref_expected() -> Result<()> {
95+
verify_that!(123, __auto_ref_eq!(123))
96+
}
97+
}

googletest/src/matcher_support/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@
1818
//! these facilities could be useful to downstream users writing custom
1919
//! matchers.
2020
21+
mod auto_ref_eq;
2122
pub(crate) mod count_elements;
2223
pub(crate) mod edit_distance;
2324
pub(crate) mod summarize_diff;
2425
pub(crate) mod zipped_iterator;
26+
27+
pub mod __internal_unstable_do_not_depend_on_these {
28+
pub use super::auto_ref_eq::internal::{ExpectedKind, MatcherKind};
29+
pub use crate::__auto_ref_eq as auto_ref_eq;
30+
}

googletest/src/matchers/field_matcher.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,21 @@
8989
/// # should_pass().unwrap();
9090
/// ```
9191
///
92+
/// If the inner matcher is `eq(...)`, it can be omitted:
93+
///
94+
/// ```
95+
/// # use googletest::prelude::*;
96+
/// #[derive(Debug)]
97+
/// struct IntField {
98+
/// int: i32
99+
/// }
100+
/// # fn should_pass() -> Result<()> {
101+
/// verify_that!(IntField{int: 32}, field!(&IntField.int, 32))?;
102+
/// # Ok(())
103+
/// # }
104+
/// # should_pass().unwrap();
105+
/// ```
106+
///
92107
/// Nested structures are *not supported*, however:
93108
///
94109
/// ```compile_fail
@@ -183,6 +198,7 @@ macro_rules! __field {
183198
macro_rules! field_internal {
184199
(&$($t:ident)::+.$field:tt, ref $m:expr) => {{
185200
use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher;
201+
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_ref_eq;
186202
field_matcher(
187203
|o: &_| {
188204
match o {
@@ -195,10 +211,11 @@ macro_rules! field_internal {
195211
}
196212
},
197213
&stringify!($field),
198-
$m)
214+
auto_ref_eq!($m))
199215
}};
200216
(&$($t:ident)::+.$field:tt, $m:expr) => {{
201217
use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher;
218+
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_ref_eq;
202219
field_matcher(
203220
|o: &&_| {
204221
match o {
@@ -211,10 +228,11 @@ macro_rules! field_internal {
211228
}
212229
},
213230
&stringify!($field),
214-
$m)
231+
auto_ref_eq!($m))
215232
}};
216233
($($t:ident)::+.$field:tt, $m:expr) => {{
217234
use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher;
235+
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_ref_eq;
218236
field_matcher(
219237
|o: &_| {
220238
match o {
@@ -227,7 +245,7 @@ macro_rules! field_internal {
227245
}
228246
},
229247
&stringify!($field),
230-
$m)
248+
auto_ref_eq!($m))
231249
}};
232250
}
233251

googletest/src/matchers/matches_pattern.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,27 @@
138138
/// # .unwrap();
139139
/// ```
140140
///
141+
/// If an inner matcher is `eq(...)`, it can be omitted:
142+
///
143+
/// ```
144+
/// # use googletest::prelude::*;
145+
/// #[derive(Debug)]
146+
/// struct MyStruct {
147+
/// a_field: String,
148+
/// another_field: String,
149+
/// }
150+
///
151+
/// let my_struct = MyStruct {
152+
/// a_field: "this".into(),
153+
/// another_field: "that".into()
154+
/// };
155+
/// verify_that!(my_struct, matches_pattern!(MyStruct {
156+
/// a_field: "this",
157+
/// another_field: "that",
158+
/// }))
159+
/// # .unwrap();
160+
/// ```
161+
///
141162
/// **Important**: The method should be pure function with a deterministic
142163
/// output and no side effects. In particular, in the event of an assertion
143164
/// failure, it will be invoked a second time, with the assertion failure output

googletest/src/matchers/property_matcher.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,25 @@
3939
/// # .unwrap();
4040
/// ```
4141
///
42+
///
43+
/// If the inner matcher is `eq(...)`, it can be omitted:
44+
///
45+
/// ```
46+
/// # use googletest::prelude::*;
47+
/// #[derive(Debug)]
48+
/// pub struct MyStruct {
49+
/// a_field: u32,
50+
/// }
51+
///
52+
/// impl MyStruct {
53+
/// pub fn get_a_field(&self) -> u32 { self.a_field }
54+
/// }
55+
///
56+
/// let value = vec![MyStruct { a_field: 100 }];
57+
/// verify_that!(value, contains(property!(&MyStruct.get_a_field(), 100)))
58+
/// # .unwrap();
59+
/// ```
60+
///
4261
/// **Important**: The method should be pure function with a deterministic
4362
/// output and no side effects. In particular, in the event of an assertion
4463
/// failure, it will be invoked a second time, with the assertion failure output
@@ -160,31 +179,35 @@ macro_rules! property_internal {
160179

161180
(&$($t:ident)::+.$method:tt($($argument:tt),* $(,)?), ref $m:expr) => {{
162181
use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher;
182+
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_ref_eq;
163183
property_ref_matcher(
164184
|o: &$($t)::+| $($t)::+::$method(o, $($argument),*),
165185
&stringify!($method($($argument),*)),
166-
$m)
186+
auto_ref_eq!($m))
167187
}};
168188
($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), ref $m:expr) => {{
169189
use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher;
190+
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_ref_eq;
170191
property_ref_matcher(
171192
|o: $($t)::+| $($t)::+::$method(o, $($argument),*),
172193
&stringify!($method($($argument),*)),
173-
$m)
194+
auto_ref_eq!($m))
174195
}};
175196
(& $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
176197
use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher;
198+
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_ref_eq;
177199
property_matcher(
178200
|o: &&$($t)::+| o.$method($($argument),*),
179201
&stringify!($method($($argument),*)),
180-
$m)
202+
auto_ref_eq!($m))
181203
}};
182204
($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
183205
use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher;
206+
use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_ref_eq;
184207
property_matcher(
185208
|o: &$($t)::+| o.$method($($argument),*),
186209
&stringify!($method($($argument),*)),
187-
$m)
210+
auto_ref_eq!($m))
188211
}};
189212
}
190213

googletest/tests/field_matcher_test.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,25 @@ fn matches_struct_ref_to_ref_binding_mode() -> Result<()> {
227227

228228
verify_that!(Strukt { a_field: "32".into() }, field!(Strukt.a_field, eq("32")))
229229
}
230+
231+
#[test]
232+
fn matches_struct_with_auto_ref_eq() -> Result<()> {
233+
#[derive(Debug)]
234+
struct Strukt {
235+
a_field: String,
236+
}
237+
238+
verify_that!(Strukt { a_field: "32".into() }, field!(Strukt.a_field, "32"))
239+
}
240+
241+
#[test]
242+
fn matches_enum_with_auto_ref_eq() -> Result<()> {
243+
#[derive(Debug)]
244+
enum Enum {
245+
Str(String),
246+
#[allow(unused)]
247+
Int(i32),
248+
}
249+
250+
verify_that!(Enum::Str("32".into()), field!(Enum::Str.0, "32"))
251+
}

googletest/tests/matches_pattern_test.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,3 +1799,18 @@ fn matches_copy_struct_property_non_copy() -> Result<()> {
17991799

18001800
verify_that!(actual, matches_pattern!(AStruct { prop(): ref eq("123") }))
18011801
}
1802+
1803+
#[test]
1804+
fn matches_struct_auto_ref_eq() -> Result<()> {
1805+
#[derive(Debug, Clone)]
1806+
struct AStruct {
1807+
int: i32,
1808+
string: String,
1809+
option: Option<i32>,
1810+
}
1811+
1812+
verify_that!(
1813+
AStruct { int: 123, string: "123".into(), option: Some(123) },
1814+
matches_pattern!(&AStruct { int: 123, string: ref "123", option: Some(123) })
1815+
)
1816+
}

googletest/tests/property_matcher_test.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,3 +278,16 @@ fn matches_ref_to_ref_with_binding_mode() -> Result<()> {
278278

279279
verify_that!(Struct, property!(Struct.property(), eq("something")))
280280
}
281+
282+
#[test]
283+
fn matches_property_auto_ref_eq() -> Result<()> {
284+
#[derive(Debug)]
285+
struct Struct;
286+
impl Struct {
287+
fn property(&self) -> String {
288+
"something".into()
289+
}
290+
}
291+
292+
verify_that!(Struct, property!(Struct.property(), "something"))
293+
}

0 commit comments

Comments
 (0)