Skip to content

Commit a5fa333

Browse files
hovinenbcopybara-github
authored andcommitted
Include the type and variant being matched in the description of the matcher created by matches_pattern!.
Previously, the description would defer immediately to the inner matcher of the respective field or property. This was problematic when matching enum variants, since the variant itself would be left out. For example, the following code: ``` enum AnEnum { A(u32), B(u32), } let value = AnEnum::B(123); verify_that!(value, matches_pattern!(AnEnum::A(eq(123))) ``` would yield the following assertion failure message: ``` Value of: value Expected: has field `0`, which is equal to 123 Actual: AnEnum::B(123), which does not have field `0` ``` The failure message does not give any indication that the expected variant is `MyEnum::A` and is therefore misleading. This change introduces a new matcher `is` which does nothing but defer to an inner matcher and add a given string to the description. With this new matcher, `matches_pattern!` now outputs the expected type or enum variant in the description: ``` Value of: value Expected: is AnEnum :: A which has field `0`, which is equal to 123 Actual: AnEnum::B(123), which does not have field `0` ``` The match explanation is admittedly still rather confusing in this case, but fixing that is outside the scope of this change. For consistency, this change also implements the new behaviour with properties as well as fields, even though properties cannot be used with enum variants. This does not import the new matcher `is` into the list of matchers included in the prelude or the top-level list of matchers for the crate. The usefulness of `is` is fairly limited, so it does not seem worthwhile to include it. It must however have public visibility because it is used in a declarative macro. PiperOrigin-RevId: 542850294
1 parent af4ae8f commit a5fa333

File tree

4 files changed

+296
-15
lines changed

4 files changed

+296
-15
lines changed

googletest/src/matchers/is_matcher.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
#![doc(hidden)]
16+
17+
use crate::matcher::{Matcher, MatcherResult};
18+
use std::{fmt::Debug, marker::PhantomData};
19+
20+
/// Matches precisely values matched by `inner`.
21+
///
22+
/// The returned matcher produces a description prefixed by the string
23+
/// `description`. This is useful in contexts where the test assertion failure
24+
/// output must include the additional description.
25+
pub fn is<'a, ActualT: Debug + 'a, InnerMatcherT: Matcher<ActualT = ActualT> + 'a>(
26+
description: &'a str,
27+
inner: InnerMatcherT,
28+
) -> impl Matcher<ActualT = ActualT> + 'a {
29+
IsMatcher { description, inner, phantom: Default::default() }
30+
}
31+
32+
struct IsMatcher<'a, ActualT, InnerMatcherT> {
33+
description: &'a str,
34+
inner: InnerMatcherT,
35+
phantom: PhantomData<ActualT>,
36+
}
37+
38+
impl<'a, ActualT: Debug, InnerMatcherT: Matcher<ActualT = ActualT>> Matcher
39+
for IsMatcher<'a, ActualT, InnerMatcherT>
40+
{
41+
type ActualT = ActualT;
42+
43+
fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
44+
self.inner.matches(actual)
45+
}
46+
47+
fn describe(&self, matcher_result: MatcherResult) -> String {
48+
match matcher_result {
49+
MatcherResult::Matches => format!(
50+
"is {} which {}",
51+
self.description,
52+
self.inner.describe(MatcherResult::Matches)
53+
),
54+
MatcherResult::DoesNotMatch => format!(
55+
"is not {} which {}",
56+
self.description,
57+
self.inner.describe(MatcherResult::Matches)
58+
),
59+
}
60+
}
61+
62+
fn explain_match(&self, actual: &Self::ActualT) -> String {
63+
self.inner.explain_match(actual)
64+
}
65+
}

googletest/src/matchers/matches_pattern.rs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -259,21 +259,30 @@ macro_rules! matches_pattern_internal {
259259
[$($struct_name:tt)*],
260260
{ $field_name:ident : $matcher:expr $(,)? }
261261
) => {
262-
all!(field!($($struct_name)*.$field_name, $matcher))
262+
$crate::matchers::is_matcher::is(
263+
stringify!($($struct_name)*),
264+
all!(field!($($struct_name)*.$field_name, $matcher))
265+
)
263266
};
264267

265268
(
266269
[$($struct_name:tt)*],
267270
{ $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
268271
) => {
269-
all!(property!($($struct_name)*.$property_name($($argument),*), $matcher))
272+
$crate::matchers::is_matcher::is(
273+
stringify!($($struct_name)*),
274+
all!(property!($($struct_name)*.$property_name($($argument),*), $matcher))
275+
)
270276
};
271277

272278
(
273279
[$($struct_name:tt)*],
274280
{ ref $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
275281
) => {
276-
all!(property!(ref $($struct_name)*.$property_name($($argument),*), $matcher))
282+
$crate::matchers::is_matcher::is(
283+
stringify!($($struct_name)*),
284+
all!(property!(ref $($struct_name)*.$property_name($($argument),*), $matcher))
285+
)
277286
};
278287

279288
(
@@ -314,32 +323,32 @@ macro_rules! matches_pattern_internal {
314323
[$($struct_name:tt)*],
315324
{ $field_name:ident : $matcher:expr $(,)? }
316325
) => {
317-
all!(
326+
$crate::matchers::is_matcher::is(stringify!($($struct_name)*), all!(
318327
$($processed)*,
319328
field!($($struct_name)*.$field_name, $matcher)
320-
)
329+
))
321330
};
322331

323332
(
324333
all!($($processed:tt)*),
325334
[$($struct_name:tt)*],
326335
{ $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
327336
) => {
328-
all!(
337+
$crate::matchers::is_matcher::is(stringify!($($struct_name)*), all!(
329338
$($processed)*,
330339
property!($($struct_name)*.$property_name($($argument),*), $matcher)
331-
)
340+
))
332341
};
333342

334343
(
335344
all!($($processed:tt)*),
336345
[$($struct_name:tt)*],
337346
{ ref $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
338347
) => {
339-
all!(
348+
$crate::matchers::is_matcher::is(stringify!($($struct_name)*), all!(
340349
$($processed)*,
341350
property!(ref $($struct_name)*.$property_name($($argument),*), $matcher)
342-
)
351+
))
343352
};
344353

345354
(
@@ -401,7 +410,10 @@ macro_rules! matches_pattern_internal {
401410
[$($struct_name:tt)*],
402411
($matcher:expr $(,)?)
403412
) => {
404-
all!(field!($($struct_name)*.0, $matcher))
413+
$crate::matchers::is_matcher::is(
414+
stringify!($($struct_name)*),
415+
all!(field!($($struct_name)*.0, $matcher))
416+
)
405417
};
406418

407419
(
@@ -424,10 +436,10 @@ macro_rules! matches_pattern_internal {
424436
$field:tt,
425437
($matcher:expr $(,)?)
426438
) => {
427-
all!(
439+
$crate::matchers::is_matcher::is(stringify!($($struct_name)*), all!(
428440
$($processed)*,
429441
field!($($struct_name)*.$field, $matcher)
430-
)
442+
))
431443
};
432444

433445
// We need to repeat this once for every supported field position, unfortunately. There appears

googletest/src/matchers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod field_matcher;
3232
pub mod ge_matcher;
3333
pub mod gt_matcher;
3434
pub mod has_entry_matcher;
35+
pub mod is_matcher;
3536
pub mod is_nan_matcher;
3637
pub mod le_matcher;
3738
pub mod len_matcher;

0 commit comments

Comments
 (0)