Skip to content

Commit 3af8347

Browse files
zhanyongwancopybara-github
authored andcommitted
Improve the failure messages of ElementsAre(), ElementsAreArray(), etc.
NOTE: if you suspect that this change breaks your build, it's likely that your tests depend on the exact messages of `ElementsAre()` and friends. The messages are implementation details of these matcher and are subject to change without notice. Depending on the messages is not supported. In that case, please rewrite your tests to avoid the dependency. When the array being matched is long, it can be hard for the user to find the mismatched element in the message generated by `ElementsAre()` or `ElementsAreArray()` - even though these matchers print the index of the mismatched element, the user still has to count to find the actual element and its corresponding matcher. With this change, these matchers will include the actual value and corresponding matcher in the failure message, making it easier for the user. Also make a small style improvement: now it's advised to write ``` EXPECT_EQ(actual, expected); ``` as opposed to ``` EXPECT_EQ(expected, actual); ``` PiperOrigin-RevId: 738039133 Change-Id: I3b94f7d01a6a4c92e2daf268df8cfb04a21d4294
1 parent 4902ea2 commit 3af8347

File tree

2 files changed

+348
-27
lines changed

2 files changed

+348
-27
lines changed

googlemock/include/gmock/gmock-matchers.h

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@
257257

258258
#include <algorithm>
259259
#include <cmath>
260+
#include <cstddef>
260261
#include <exception>
261262
#include <functional>
262263
#include <initializer_list>
@@ -2097,11 +2098,11 @@ class WhenDynamicCastToMatcher<To&> : public WhenDynamicCastToMatcherBase<To&> {
20972098
template <typename Class, typename FieldType>
20982099
class FieldMatcher {
20992100
public:
2100-
FieldMatcher(FieldType Class::*field,
2101+
FieldMatcher(FieldType Class::* field,
21012102
const Matcher<const FieldType&>& matcher)
21022103
: field_(field), matcher_(matcher), whose_field_("whose given field ") {}
21032104

2104-
FieldMatcher(const std::string& field_name, FieldType Class::*field,
2105+
FieldMatcher(const std::string& field_name, FieldType Class::* field,
21052106
const Matcher<const FieldType&>& matcher)
21062107
: field_(field),
21072108
matcher_(matcher),
@@ -2145,7 +2146,7 @@ class FieldMatcher {
21452146
return MatchAndExplainImpl(std::false_type(), *p, listener);
21462147
}
21472148

2148-
const FieldType Class::*field_;
2149+
const FieldType Class::* field_;
21492150
const Matcher<const FieldType&> matcher_;
21502151

21512152
// Contains either "whose given field " if the name of the field is unknown
@@ -3291,8 +3292,8 @@ class PairMatcher {
32913292
};
32923293

32933294
template <typename T, size_t... I>
3294-
auto UnpackStructImpl(const T& t, std::index_sequence<I...>,
3295-
int) -> decltype(std::tie(get<I>(t)...)) {
3295+
auto UnpackStructImpl(const T& t, std::index_sequence<I...>, int)
3296+
-> decltype(std::tie(get<I>(t)...)) {
32963297
static_assert(std::tuple_size<T>::value == sizeof...(I),
32973298
"Number of arguments doesn't match the number of fields.");
32983299
return std::tie(get<I>(t)...);
@@ -3581,7 +3582,7 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
35813582
StlContainerReference stl_container = View::ConstReference(container);
35823583
auto it = stl_container.begin();
35833584
size_t exam_pos = 0;
3584-
bool mismatch_found = false; // Have we found a mismatched element yet?
3585+
bool unmatched_found = false;
35853586

35863587
// Go through the elements and matchers in pairs, until we reach
35873588
// the end of either the elements or the matchers, or until we find a
@@ -3597,11 +3598,23 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
35973598
}
35983599

35993600
if (!match) {
3600-
mismatch_found = true;
3601+
unmatched_found = true;
3602+
// We cannot store the iterator for the unmatched element to be used
3603+
// later, as some users use ElementsAre() with a "container" whose
3604+
// iterator is not copy-constructible or copy-assignable.
3605+
//
3606+
// We cannot store a pointer to the element either, as some container's
3607+
// iterators return a temporary.
3608+
//
3609+
// We cannot store the element itself either, as the element may not be
3610+
// copyable.
3611+
//
3612+
// Therefore, we just remember the index of the unmatched element,
3613+
// and use it later to print the unmatched element.
36013614
break;
36023615
}
36033616
}
3604-
// If mismatch_found is true, 'exam_pos' is the index of the mismatch.
3617+
// If unmatched_found is true, exam_pos is the index of the mismatch.
36053618

36063619
// Find how many elements the actual container has. We avoid
36073620
// calling size() s.t. this code works for stream-like "containers"
@@ -3622,10 +3635,27 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
36223635
return false;
36233636
}
36243637

3625-
if (mismatch_found) {
3638+
if (unmatched_found) {
36263639
// The element count matches, but the exam_pos-th element doesn't match.
36273640
if (listener_interested) {
3628-
*listener << "whose element #" << exam_pos << " doesn't match";
3641+
// Find the unmatched element.
3642+
auto unmatched_it = stl_container.begin();
3643+
// We cannot call std::advance() on the iterator, as some users use
3644+
// ElementsAre() with a "container" whose iterator is incompatible with
3645+
// std::advance() (e.g. it may not have the difference_type member
3646+
// type).
3647+
for (size_t i = 0; i != exam_pos; ++i) {
3648+
++unmatched_it;
3649+
}
3650+
3651+
// If the array is long or the elements' print-out is large, it may be
3652+
// hard for the user to find the mismatched element and its
3653+
// corresponding matcher description. Therefore we print the index, the
3654+
// value of the mismatched element, and the corresponding matcher
3655+
// description to ease debugging.
3656+
*listener << "whose element #" << exam_pos << " ("
3657+
<< PrintToString(*unmatched_it) << ") ";
3658+
matchers_[exam_pos].DescribeNegationTo(listener->stream());
36293659
PrintIfNotEmpty(explanations[exam_pos], listener->stream());
36303660
}
36313661
return false;
@@ -4031,15 +4061,15 @@ GTEST_API_ std::string FormatMatcherDescription(
40314061
// Overloads to support `OptionalMatcher` being used with a type that either
40324062
// supports implicit conversion to bool or a `has_value()` method.
40334063
template <typename Optional>
4034-
auto IsOptionalEngaged(const Optional& optional,
4035-
Rank1) -> decltype(!!optional) {
4064+
auto IsOptionalEngaged(const Optional& optional, Rank1)
4065+
-> decltype(!!optional) {
40364066
// The use of double-negation here is to preserve historical behavior where
40374067
// the matcher used `operator!` rather than directly using `operator bool`.
40384068
return !static_cast<bool>(!optional);
40394069
}
40404070
template <typename Optional>
4041-
auto IsOptionalEngaged(const Optional& optional,
4042-
Rank0) -> decltype(!optional.has_value()) {
4071+
auto IsOptionalEngaged(const Optional& optional, Rank0)
4072+
-> decltype(!optional.has_value()) {
40434073
return optional.has_value();
40444074
}
40454075

@@ -4567,7 +4597,7 @@ WhenDynamicCastTo(const Matcher<To>& inner_matcher) {
45674597
// matches a Foo object x if and only if x.number >= 5.
45684598
template <typename Class, typename FieldType, typename FieldMatcher>
45694599
inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
4570-
FieldType Class::*field, const FieldMatcher& matcher) {
4600+
FieldType Class::* field, const FieldMatcher& matcher) {
45714601
return MakePolymorphicMatcher(internal::FieldMatcher<Class, FieldType>(
45724602
field, MatcherCast<const FieldType&>(matcher)));
45734603
// The call to MatcherCast() is required for supporting inner
@@ -4580,7 +4610,7 @@ inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
45804610
// messages.
45814611
template <typename Class, typename FieldType, typename FieldMatcher>
45824612
inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
4583-
const std::string& field_name, FieldType Class::*field,
4613+
const std::string& field_name, FieldType Class::* field,
45844614
const FieldMatcher& matcher) {
45854615
return MakePolymorphicMatcher(internal::FieldMatcher<Class, FieldType>(
45864616
field_name, field, MatcherCast<const FieldType&>(matcher)));

0 commit comments

Comments
 (0)