1 /*
2 * Copyright 2018 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // gMock matchers used to validate protocol buffer arguments.
18
19 // WHAT THIS IS
20 // ============
21 //
22 // This library defines the following matchers in the ::android namespace:
23 //
24 // EqualsProto(pb) The argument equals pb.
25 // EqualsInitializedProto(pb) The argument is initialized and equals pb.
26 // EquivToProto(pb) The argument is equivalent to pb.
27 // EquivToInitializedProto(pb) The argument is initialized and equivalent
28 // to pb.
29 // IsInitializedProto() The argument is an initialized protobuf.
30 //
31 // where:
32 //
33 // - pb can be either a protobuf value or a human-readable string
34 // representation of it.
35 // - When pb is a string, the matcher can optionally accept a
36 // template argument for the type of the protobuf,
37 // e.g. EqualsProto<Foo>("foo: 1").
38 // - "equals" is defined as the argument's Equals(pb) method returns true.
39 // - "equivalent to" is defined as the argument's Equivalent(pb) method
40 // returns true.
41 // - "initialized" means that the argument's IsInitialized() method returns
42 // true.
43 //
44 // These matchers can match either a protobuf value or a pointer to
45 // it. They make a copy of pb, and thus can out-live pb. When the
46 // match fails, the matchers print a detailed message (the value of
47 // the actual protobuf, the value of the expected protobuf, and which
48 // fields are different).
49 //
50 // This library also defines the following matcher transformer
51 // functions in the ::android::proto namespace:
52 //
53 // Approximately(m, margin, fraction)
54 // The same as m, except that it compares
55 // floating-point fields approximately (using
56 // google::protobuf::util::MessageDifferencer's APPROXIMATE
57 // comparison option). m can be any of the
58 // Equals* and EquivTo* protobuf matchers above. If margin
59 // is specified, floats and doubles will be considered
60 // approximately equal if they are within that margin, i.e.
61 // abs(expected - actual) <= margin. If fraction is
62 // specified, floats and doubles will be considered
63 // approximately equal if they are within a fraction of
64 // their magnitude, i.e. abs(expected - actual) <=
65 // fraction * max(abs(expected), abs(actual)). Two fields
66 // will be considered equal if they're within the fraction
67 // _or_ within the margin, so omitting or setting the
68 // fraction to 0.0 will only check against the margin.
69 // Similarly, setting the margin to 0.0 will only check
70 // using the fraction. If margin and fraction are omitted,
71 // MathLimits<T>::kStdError for that type (T=float or
72 // T=double) is used for both the margin and fraction.
73 // TreatingNaNsAsEqual(m)
74 // The same as m, except that treats floating-point fields
75 // that are NaN as equal. m can be any of the Equals* and
76 // EquivTo* protobuf matchers above.
77 // IgnoringFields(fields, m)
78 // The same as m, except the specified fields will be
79 // ignored when matching (using
80 // google::protobuf::util::MessageDifferencer::IgnoreField).
81 // fields is represented as a container or an initializer
82 // list of strings and each element is specified by their
83 // fully qualified names, i.e., the names corresponding to
84 // FieldDescriptor.full_name(). m can be
85 // any of the Equals* and EquivTo* protobuf matchers above.
86 // It can also be any of the transformer matchers listed
87 // here (e.g. Approximately, TreatingNaNsAsEqual) as long as
88 // the intent of the each concatenated matcher is mutually
89 // exclusive (e.g. using IgnoringFields in conjunction with
90 // Partially can have different results depending on whether
91 // the fields specified in IgnoringFields is part of the
92 // fields covered by Partially).
93 // IgnoringRepeatedFieldOrdering(m)
94 // The same as m, except that it ignores the relative
95 // ordering of elements within each repeated field in m.
96 // See
97 // google::protobuf::util::MessageDifferencer::TreatAsSet()
98 // for more details.
99 // Partially(m)
100 // The same as m, except that only fields present in
101 // the expected protobuf are considered (using
102 // google::protobuf::util::MessageDifferencer's PARTIAL
103 // comparison option). m can be any of the
104 // Equals* and EquivTo* protobuf matchers above.
105 // WhenDeserialized(typed_pb_matcher)
106 // The string argument is a serialization of a
107 // protobuf that matches typed_pb_matcher.
108 // typed_pb_matcher can be an Equals* or EquivTo*
109 // protobuf matcher (possibly with Approximately()
110 // or Partially() modifiers) where the type of the
111 // protobuf is known at run time (e.g. it cannot
112 // be EqualsProto("...") as it's unclear what type
113 // the string represents).
114 // WhenDeserializedAs<PB>(pb_matcher)
115 // Like WhenDeserialized(), except that the type
116 // of the deserialized protobuf must be PB. Since
117 // the protobuf type is known, pb_matcher can be *any*
118 // valid protobuf matcher, including EqualsProto("...").
119 //
120 // Approximately(), TreatingNaNsAsEqual(), Partially(), IgnoringFields(), and
121 // IgnoringRepeatedFieldOrdering() can be combined (nested)
122 // and the composition order is irrelevant:
123 //
124 // Approximately(Partially(EquivToProto(pb)))
125 // and
126 // Partially(Approximately(EquivToProto(pb)))
127 // are the same thing.
128 //
129 // EXAMPLES
130 // ========
131 //
132 // using ::android::EqualsProto;
133 // using ::android::EquivToProto;
134 // using ::android::proto::Approximately;
135 // using ::android::proto::Partially;
136 // using ::android::proto::WhenDeserialized;
137 //
138 // // my_pb.Equals(expected_pb).
139 // EXPECT_THAT(my_pb, EqualsProto(expected_pb));
140 //
141 // // my_pb is equivalent to a protobuf whose foo field is 1 and
142 // // whose bar field is "x".
143 // EXPECT_THAT(my_pb, EquivToProto("foo: 1 "
144 // "bar: 'x'"));
145 //
146 // // my_pb is equal to expected_pb, comparing all floating-point
147 // // fields approximately.
148 // EXPECT_THAT(my_pb, Approximately(EqualsProto(expected_pb)));
149 //
150 // // my_pb is equivalent to expected_pb. A field is ignored in the
151 // // comparison if it's present in my_pb but not in expected_pb.
152 // EXPECT_THAT(my_pb, Partially(EquivToProto(expected_pb)));
153 //
154 // string data;
155 // my_pb.SerializeToString(&data);
156 // // data can be deserialized to a protobuf that equals expected_pb.
157 // EXPECT_THAT(data, WhenDeserialized(EqualsProto(expected_pb)));
158 // // The following line doesn't compile, as the matcher doesn't know
159 // // the type of the protobuf.
160 // // EXPECT_THAT(data, WhenDeserialized(EqualsProto("foo: 1")));
161
162 #pragma once
163
164 #include <initializer_list>
165 #include <iostream> // NOLINT
166 #include <memory>
167 #include <sstream> // NOLINT
168 #include <string> // NOLINT
169 #include <string_view>
170 #include <vector> // NOLINT
171
172 #include "gmock/gmock-matchers.h"
173 #include "gmock/gmock-more-matchers.h"
174 #include "google/protobuf/descriptor.h"
175 #include "google/protobuf/io/zero_copy_stream.h"
176 #include "google/protobuf/io/zero_copy_stream_impl.h"
177 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
178 #include "google/protobuf/message.h"
179 #include "google/protobuf/text_format.h"
180 #include "google/protobuf/util/field_comparator.h"
181 #include "google/protobuf/util/message_differencer.h"
182
183 namespace android {
184
185 namespace internal {
186
187 // Utilities.
188
189 // How to compare two fields (equal vs. equivalent).
190 typedef google::protobuf::util::MessageDifferencer::MessageFieldComparison ProtoFieldComparison;
191
192 // How to compare two floating-points (exact vs. approximate).
193 typedef google::protobuf::util::DefaultFieldComparator::FloatComparison ProtoFloatComparison;
194
195 // How to compare repeated fields (whether the order of elements matters).
196 typedef google::protobuf::util::MessageDifferencer::RepeatedFieldComparison RepeatedFieldComparison;
197
198 // Whether to compare all fields (full) or only fields present in the
199 // expected protobuf (partial).
200 typedef google::protobuf::util::MessageDifferencer::Scope ProtoComparisonScope;
201
202 const ProtoFieldComparison kProtoEqual = google::protobuf::util::MessageDifferencer::EQUAL;
203 const ProtoFieldComparison kProtoEquiv = google::protobuf::util::MessageDifferencer::EQUIVALENT;
204 const ProtoFloatComparison kProtoExact = google::protobuf::util::DefaultFieldComparator::EXACT;
205 const ProtoFloatComparison kProtoApproximate =
206 google::protobuf::util::DefaultFieldComparator::APPROXIMATE;
207 const RepeatedFieldComparison kProtoCompareRepeatedFieldsRespectOrdering =
208 google::protobuf::util::MessageDifferencer::AS_LIST;
209 const RepeatedFieldComparison kProtoCompareRepeatedFieldsIgnoringOrdering =
210 google::protobuf::util::MessageDifferencer::AS_SET;
211 const ProtoComparisonScope kProtoFull = google::protobuf::util::MessageDifferencer::FULL;
212 const ProtoComparisonScope kProtoPartial = google::protobuf::util::MessageDifferencer::PARTIAL;
213
214 // Options for comparing two protobufs.
215 struct ProtoComparison {
ProtoComparisonProtoComparison216 ProtoComparison()
217 : field_comp(kProtoEqual),
218 float_comp(kProtoExact),
219 treating_nan_as_equal(false),
220 has_custom_margin(false),
221 has_custom_fraction(false),
222 repeated_field_comp(kProtoCompareRepeatedFieldsRespectOrdering),
223 scope(kProtoFull),
224 float_margin(0.0),
225 float_fraction(0.0) {}
226
227 ProtoFieldComparison field_comp;
228 ProtoFloatComparison float_comp;
229 bool treating_nan_as_equal;
230 bool has_custom_margin; // only used when float_comp = APPROXIMATE
231 bool has_custom_fraction; // only used when float_comp = APPROXIMATE
232 RepeatedFieldComparison repeated_field_comp;
233 ProtoComparisonScope scope;
234 double float_margin; // only used when has_custom_margin is set.
235 double float_fraction; // only used when has_custom_fraction is set.
236 std::vector<std::string> ignore_fields;
237 };
238
239 // Whether the protobuf must be initialized.
240 const bool kMustBeInitialized = true;
241 const bool kMayBeUninitialized = false;
242
243 // Parses the TextFormat representation of a protobuf, allowing required fields
244 // to be missing. Returns true iff successful.
245 bool ParsePartialFromAscii(const std::string& pb_ascii, google::protobuf::Message* proto,
246 std::string* error_text);
247
248 // Returns a protobuf of type Proto by parsing the given TextFormat
249 // representation of it. Required fields can be missing, in which case the
250 // returned protobuf will not be fully initialized.
251 template <class Proto>
MakePartialProtoFromAscii(const std::string & str)252 Proto MakePartialProtoFromAscii(const std::string& str) {
253 Proto proto;
254 std::string error_text;
255 if (!ParsePartialFromAscii(str, &proto, &error_text)) {
256 std::cerr << "Failed to parse \"" << str << "\" as a " << proto.GetDescriptor()->full_name()
257 << ":\n"
258 << error_text;
259 }
260 return proto;
261 }
262
263 // Returns true iff p and q can be compared (i.e. have the same descriptor).
264 bool ProtoComparable(const google::protobuf::Message& p, const google::protobuf::Message& q);
265
266 // Returns true iff actual and expected are comparable and match. The
267 // comp argument specifies how the two are compared.
268 bool ProtoCompare(const ProtoComparison& comp, const google::protobuf::Message& actual,
269 const google::protobuf::Message& expected);
270
271 // Overload for ProtoCompare where the expected message is specified as a text
272 // proto. If the text cannot be parsed as a message of the same type as the
273 // actual message, a // DCHECK failure will cause the test to fail and no subsequent
274 // tests will be run.
275 template <typename Proto>
ProtoCompare(const ProtoComparison & comp,const Proto & actual,const std::string & expected)276 inline bool ProtoCompare(const ProtoComparison& comp, const Proto& actual,
277 const std::string& expected) {
278 return ProtoCompare(comp, actual, MakePartialProtoFromAscii<Proto>(expected));
279 }
280
281 // Describes the types of the expected and the actual protocol buffer.
282 std::string DescribeTypes(const google::protobuf::Message& expected,
283 const google::protobuf::Message& actual);
284
285 // Prints the protocol buffer pointed to by proto.
286 std::string PrintProtoPointee(const google::protobuf::Message* proto);
287
288 // Describes the differences between the two protocol buffers.
289 std::string DescribeDiff(const ProtoComparison& comp, const google::protobuf::Message& actual,
290 const google::protobuf::Message& expected);
291
292 // Common code for implementing EqualsProto, EquivToProto,
293 // EqualsInitializedProto, and EquivToInitializedProto.
294 class ProtoMatcherBase {
295 public:
ProtoMatcherBase(bool must_be_initialized,const ProtoComparison & comp)296 ProtoMatcherBase(bool must_be_initialized, // Must the argument be fully
297 // initialized?
298 const ProtoComparison& comp) // How to compare the two protobufs.
299 : must_be_initialized_(must_be_initialized), comp_(new auto(comp)) {}
300
ProtoMatcherBase(const ProtoMatcherBase & other)301 ProtoMatcherBase(const ProtoMatcherBase& other)
302 : must_be_initialized_(other.must_be_initialized_), comp_(new auto(*other.comp_)) {}
303
304 ProtoMatcherBase(ProtoMatcherBase&& other) = default;
305
~ProtoMatcherBase()306 virtual ~ProtoMatcherBase() {}
307
308 // Prints the expected protocol buffer.
309 virtual void PrintExpectedTo(::std::ostream* os) const = 0;
310
311 // Returns the expected value as a protobuf object; if the object
312 // cannot be created (e.g. in ProtoStringMatcher), explains why to
313 // 'listener' and returns NULL. The caller must call
314 // DeleteExpectedProto() on the returned value later.
315 virtual const google::protobuf::Message* CreateExpectedProto(
316 const google::protobuf::Message& arg, // For determining the type of the
317 // expected protobuf.
318 ::testing::MatchResultListener* listener) const = 0;
319
320 // Deletes the given expected protobuf, which must be obtained from
321 // a call to CreateExpectedProto() earlier.
322 virtual void DeleteExpectedProto(const google::protobuf::Message* expected) const = 0;
323
324 // Makes this matcher compare floating-points approximately.
SetCompareApproximately()325 void SetCompareApproximately() { comp_->float_comp = kProtoApproximate; }
326
327 // Makes this matcher treating NaNs as equal when comparing floating-points.
SetCompareTreatingNaNsAsEqual()328 void SetCompareTreatingNaNsAsEqual() { comp_->treating_nan_as_equal = true; }
329
330 // Makes this matcher ignore string elements specified by their fully
331 // qualified names, i.e., names corresponding to
332 // FieldDescriptor.full_name().
333 template <class Iterator>
AddCompareIgnoringFields(Iterator first,Iterator last)334 void AddCompareIgnoringFields(Iterator first, Iterator last) {
335 comp_->ignore_fields.insert(comp_->ignore_fields.end(), first, last);
336 }
337
338 // Makes this matcher compare repeated fields ignoring ordering of elements.
SetCompareRepeatedFieldsIgnoringOrdering()339 void SetCompareRepeatedFieldsIgnoringOrdering() {
340 comp_->repeated_field_comp = kProtoCompareRepeatedFieldsIgnoringOrdering;
341 }
342
343 // Sets the margin of error for approximate floating point comparison.
SetMargin(double margin)344 void SetMargin(double margin) {
345 comp_->has_custom_margin = true;
346 comp_->float_margin = margin;
347 }
348
349 // Sets the relative fraction of error for approximate floating point
350 // comparison.
SetFraction(double fraction)351 void SetFraction(double fraction) {
352 comp_->has_custom_fraction = true;
353 comp_->float_fraction = fraction;
354 }
355
356 // Makes this matcher compare protobufs partially.
SetComparePartially()357 void SetComparePartially() { comp_->scope = kProtoPartial; }
358
MatchAndExplain(const google::protobuf::Message & arg,::testing::MatchResultListener * listener)359 bool MatchAndExplain(const google::protobuf::Message& arg,
360 ::testing::MatchResultListener* listener) const {
361 return MatchAndExplain(arg, false, listener);
362 }
363
MatchAndExplain(const google::protobuf::Message * arg,::testing::MatchResultListener * listener)364 bool MatchAndExplain(const google::protobuf::Message* arg,
365 ::testing::MatchResultListener* listener) const {
366 return (arg != NULL) && MatchAndExplain(*arg, true, listener);
367 }
368
369 // Describes the expected relation between the actual protobuf and
370 // the expected one.
DescribeRelationToExpectedProto(::std::ostream * os)371 void DescribeRelationToExpectedProto(::std::ostream* os) const {
372 if (comp_->repeated_field_comp == kProtoCompareRepeatedFieldsIgnoringOrdering) {
373 *os << "(ignoring repeated field ordering) ";
374 }
375 if (!comp_->ignore_fields.empty()) {
376 *os << "(ignoring fields: ";
377 const char* sep = "";
378 for (size_t i = 0; i < comp_->ignore_fields.size(); ++i, sep = ", ")
379 *os << sep << comp_->ignore_fields[i];
380 *os << ") ";
381 }
382 if (comp_->float_comp == kProtoApproximate) {
383 *os << "approximately ";
384 if (comp_->has_custom_margin || comp_->has_custom_fraction) {
385 *os << "(";
386 if (comp_->has_custom_margin) {
387 std::stringstream ss;
388 ss << std::setprecision(std::numeric_limits<double>::digits10 + 2)
389 << comp_->float_margin;
390 *os << "absolute error of float or double fields <= " << ss.str();
391 }
392 if (comp_->has_custom_margin && comp_->has_custom_fraction) {
393 *os << " or ";
394 }
395 if (comp_->has_custom_fraction) {
396 std::stringstream ss;
397 ss << std::setprecision(std::numeric_limits<double>::digits10 + 2)
398 << comp_->float_fraction;
399 *os << "relative error of float or double fields <= " << ss.str();
400 }
401 *os << ") ";
402 }
403 }
404
405 *os << (comp_->scope == kProtoPartial ? "partially " : "")
406 << (comp_->field_comp == kProtoEqual ? "equal" : "equivalent")
407 << (comp_->treating_nan_as_equal ? " (treating NaNs as equal)" : "") << " to ";
408 PrintExpectedTo(os);
409 }
410
DescribeTo(::std::ostream * os)411 void DescribeTo(::std::ostream* os) const {
412 *os << "is " << (must_be_initialized_ ? "fully initialized and " : "");
413 DescribeRelationToExpectedProto(os);
414 }
415
DescribeNegationTo(::std::ostream * os)416 void DescribeNegationTo(::std::ostream* os) const {
417 *os << "is " << (must_be_initialized_ ? "not fully initialized or " : "") << "not ";
418 DescribeRelationToExpectedProto(os);
419 }
420
must_be_initialized()421 bool must_be_initialized() const { return must_be_initialized_; }
422
comp()423 const ProtoComparison& comp() const { return *comp_; }
424
425 private:
426 bool MatchAndExplain(const google::protobuf::Message& arg, bool is_matcher_for_pointer,
427 ::testing::MatchResultListener* listener) const;
428
429 const bool must_be_initialized_;
430 std::unique_ptr<ProtoComparison> comp_;
431 };
432
433 // Returns a copy of the given proto2 message.
CloneProto2(const google::protobuf::Message & src)434 inline google::protobuf::Message* CloneProto2(const google::protobuf::Message& src) {
435 google::protobuf::Message* clone = src.New();
436 clone->CopyFrom(src);
437 return clone;
438 }
439
440 // Implements EqualsProto, EquivToProto, EqualsInitializedProto, and
441 // EquivToInitializedProto, where the matcher parameter is a protobuf.
442 class ProtoMatcher : public ProtoMatcherBase {
443 public:
ProtoMatcher(const google::protobuf::Message & expected,bool must_be_initialized,const ProtoComparison & comp)444 ProtoMatcher(const google::protobuf::Message& expected, // The expected protobuf.
445 bool must_be_initialized, // Must the argument be fully
446 // initialized?
447 const ProtoComparison& comp) // How to compare the two protobufs.
448 : ProtoMatcherBase(must_be_initialized, comp), expected_(CloneProto2(expected)) {
449 if (must_be_initialized) {
450 if (expected.IsInitialized()) {
451 std::cerr << "The protocol buffer given to *InitializedProto() "
452 << "must itself be initialized, but the following required "
453 "fields "
454 << "are missing: " << expected.InitializationErrorString() << ".";
455 }
456 }
457 }
458
PrintExpectedTo(::std::ostream * os)459 virtual void PrintExpectedTo(::std::ostream* os) const {
460 *os << expected_->GetDescriptor()->full_name() << " ";
461 ::testing::internal::UniversalPrint(*expected_, os);
462 }
463
CreateExpectedProto(const google::protobuf::Message &,::testing::MatchResultListener *)464 virtual const google::protobuf::Message* CreateExpectedProto(
465 const google::protobuf::Message& /* arg */,
466 ::testing::MatchResultListener* /* listener */) const {
467 return expected_.get();
468 }
469
DeleteExpectedProto(const google::protobuf::Message * expected)470 virtual void DeleteExpectedProto(const google::protobuf::Message* expected) const {}
471
expected()472 const std::shared_ptr<const google::protobuf::Message>& expected() const { return expected_; }
473
474 private:
475 const std::shared_ptr<const google::protobuf::Message> expected_;
476 };
477
478 // Implements EqualsProto, EquivToProto, EqualsInitializedProto, and
479 // EquivToInitializedProto, where the matcher parameter is a string.
480 class ProtoStringMatcher : public ProtoMatcherBase {
481 public:
ProtoStringMatcher(const std::string & expected,bool must_be_initialized,const ProtoComparison comp)482 ProtoStringMatcher(const std::string& expected, // The text representing the expected protobuf.
483 bool must_be_initialized, // Must the argument be fully
484 // initialized?
485 const ProtoComparison comp) // How to compare the two protobufs.
486 : ProtoMatcherBase(must_be_initialized, comp), expected_(expected) {}
487
488 // Parses the expected string as a protobuf of the same type as arg,
489 // and returns the parsed protobuf (or NULL when the parse fails).
490 // The caller must call DeleteExpectedProto() on the return value
491 // later.
CreateExpectedProto(const google::protobuf::Message & arg,::testing::MatchResultListener * listener)492 virtual const google::protobuf::Message* CreateExpectedProto(
493 const google::protobuf::Message& arg, ::testing::MatchResultListener* listener) const {
494 google::protobuf::Message* expected_proto = arg.New();
495 // We don't insist that the expected string parses as an
496 // *initialized* protobuf. Otherwise EqualsProto("...") may
497 // wrongfully fail when the actual protobuf is not fully
498 // initialized. If the user wants to ensure that the actual
499 // protobuf is initialized, they should use
500 // EqualsInitializedProto("...") instead of EqualsProto("..."),
501 // and the MatchAndExplain() function in ProtoMatcherBase will
502 // enforce it.
503 std::string error_text;
504 if (ParsePartialFromAscii(expected_, expected_proto, &error_text)) {
505 return expected_proto;
506 } else {
507 delete expected_proto;
508 if (listener->IsInterested()) {
509 *listener << "where ";
510 PrintExpectedTo(listener->stream());
511 *listener << " doesn't parse as a " << arg.GetDescriptor()->full_name() << ":\n"
512 << error_text;
513 }
514 return NULL;
515 }
516 }
517
DeleteExpectedProto(const google::protobuf::Message * expected)518 virtual void DeleteExpectedProto(const google::protobuf::Message* expected) const {
519 delete expected;
520 }
521
PrintExpectedTo(::std::ostream * os)522 virtual void PrintExpectedTo(::std::ostream* os) const { *os << "<" << expected_ << ">"; }
523
524 private:
525 const std::string expected_;
526 };
527
528 typedef ::testing::PolymorphicMatcher<ProtoMatcher> PolymorphicProtoMatcher;
529
530 // Common code for implementing WhenDeserialized(proto_matcher) and
531 // WhenDeserializedAs<PB>(proto_matcher).
532 template <class Proto>
533 class WhenDeserializedMatcherBase {
534 public:
535 typedef ::testing::Matcher<const Proto&> InnerMatcher;
536
WhenDeserializedMatcherBase(const InnerMatcher & proto_matcher)537 explicit WhenDeserializedMatcherBase(const InnerMatcher& proto_matcher)
538 : proto_matcher_(proto_matcher) {}
539
~WhenDeserializedMatcherBase()540 virtual ~WhenDeserializedMatcherBase() {}
541
542 // Creates an empty protobuf with the expected type.
543 virtual Proto* MakeEmptyProto() const = 0;
544
545 // Type name of the expected protobuf.
546 virtual std::string ExpectedTypeName() const = 0;
547
548 // Name of the type argument given to WhenDeserializedAs<>(), or
549 // "protobuf" for WhenDeserialized().
550 virtual std::string TypeArgName() const = 0;
551
552 // Deserializes the string as a protobuf of the same type as the expected
553 // protobuf.
Deserialize(google::protobuf::io::ZeroCopyInputStream * input)554 Proto* Deserialize(google::protobuf::io::ZeroCopyInputStream* input) const {
555 Proto* proto = MakeEmptyProto();
556 // ParsePartialFromString() parses a serialized representation of a
557 // protobuf, allowing required fields to be missing. This means
558 // that we don't insist on the parsed protobuf being fully
559 // initialized. This allows the user to choose whether it should
560 // be initialized using EqualsProto vs EqualsInitializedProto, for
561 // example.
562 if (proto->ParsePartialFromZeroCopyStream(input)) {
563 return proto;
564 } else {
565 delete proto;
566 return NULL;
567 }
568 }
569
DescribeTo(::std::ostream * os)570 void DescribeTo(::std::ostream* os) const {
571 *os << "can be deserialized as a " << TypeArgName() << " that ";
572 proto_matcher_.DescribeTo(os);
573 }
574
DescribeNegationTo(::std::ostream * os)575 void DescribeNegationTo(::std::ostream* os) const {
576 *os << "cannot be deserialized as a " << TypeArgName() << " that ";
577 proto_matcher_.DescribeTo(os);
578 }
579
MatchAndExplain(google::protobuf::io::ZeroCopyInputStream * arg,::testing::MatchResultListener * listener)580 bool MatchAndExplain(google::protobuf::io::ZeroCopyInputStream* arg,
581 ::testing::MatchResultListener* listener) const {
582 // Deserializes the string arg as a protobuf of the same type as the
583 // expected protobuf.
584 ::std::unique_ptr<const Proto> deserialized_arg(Deserialize(arg));
585 if (!listener->IsInterested()) {
586 // No need to explain the match result.
587 return (deserialized_arg != NULL) && proto_matcher_.Matches(*deserialized_arg);
588 }
589
590 ::std::ostream* const os = listener->stream();
591 if (deserialized_arg == NULL) {
592 *os << "which cannot be deserialized as a " << ExpectedTypeName();
593 return false;
594 }
595
596 *os << "which deserializes to ";
597 UniversalPrint(*deserialized_arg, os);
598
599 ::testing::StringMatchResultListener inner_listener;
600 const bool match = proto_matcher_.MatchAndExplain(*deserialized_arg, &inner_listener);
601 const std::string explain = inner_listener.str();
602 if (explain != "") {
603 *os << ",\n" << explain;
604 }
605
606 return match;
607 }
608
MatchAndExplain(const std::string & str,::testing::MatchResultListener * listener)609 bool MatchAndExplain(const std::string& str, ::testing::MatchResultListener* listener) const {
610 google::protobuf::io::ArrayInputStream input(str.data(), str.size());
611 return MatchAndExplain(&input, listener);
612 }
613
MatchAndExplain(std::string_view sp,::testing::MatchResultListener * listener)614 bool MatchAndExplain(std::string_view sp, ::testing::MatchResultListener* listener) const {
615 google::protobuf::io::ArrayInputStream input(sp.data(), sp.size());
616 return MatchAndExplain(&input, listener);
617 }
618
MatchAndExplain(const char * str,::testing::MatchResultListener * listener)619 bool MatchAndExplain(const char* str, ::testing::MatchResultListener* listener) const {
620 google::protobuf::io::ArrayInputStream input(str, strlen(str));
621 return MatchAndExplain(&input, listener);
622 }
623
624 private:
625 const InnerMatcher proto_matcher_;
626 };
627
628 // Implements WhenDeserialized(proto_matcher).
629 class WhenDeserializedMatcher : public WhenDeserializedMatcherBase<google::protobuf::Message> {
630 public
WhenDeserializedMatcher(const PolymorphicProtoMatcher & proto_matcher)631 : explicit WhenDeserializedMatcher(const PolymorphicProtoMatcher& proto_matcher)
632 : WhenDeserializedMatcherBase<google::protobuf::Message>(proto_matcher),
633 expected_proto_(proto_matcher.impl().expected()) {}
634
MakeEmptyProto()635 virtual google::protobuf::Message* MakeEmptyProto() const { return expected_proto_->New(); }
636
ExpectedTypeName()637 virtual std::string ExpectedTypeName() const {
638 return expected_proto_->GetDescriptor()->full_name();
639 }
640
TypeArgName()641 virtual std::string TypeArgName() const { return "protobuf"; }
642
643 private:
644 // The expected protobuf specified in the inner matcher
645 // (proto_matcher_). We only need a std::shared_ptr to it instead of
646 // making a copy, as the expected protobuf will never be changed
647 // once created.
648 const std::shared_ptr<const google::protobuf::Message> expected_proto_;
649 };
650
651 // Implements WhenDeserializedAs<Proto>(proto_matcher).
652 template <class Proto>
653 class WhenDeserializedAsMatcher : public WhenDeserializedMatcherBase<Proto> {
654 public:
655 typedef ::testing::Matcher<const Proto&> InnerMatcher;
656
WhenDeserializedAsMatcher(const InnerMatcher & inner_matcher)657 explicit WhenDeserializedAsMatcher(const InnerMatcher& inner_matcher)
658 : WhenDeserializedMatcherBase<Proto>(inner_matcher) {}
659
MakeEmptyProto()660 virtual Proto* MakeEmptyProto() const { return new Proto; }
661
ExpectedTypeName()662 virtual std::string ExpectedTypeName() const { return Proto().GetDescriptor()->full_name(); }
663
TypeArgName()664 virtual std::string TypeArgName() const { return ExpectedTypeName(); }
665 };
666
667 // Implements the IsInitializedProto matcher, which is used to verify that a
668 // protocol buffer is valid using the IsInitialized method.
669 class IsInitializedProtoMatcher {
670 public:
DescribeTo(::std::ostream * os)671 void DescribeTo(::std::ostream* os) const { *os << "is a fully initialized protocol buffer"; }
672
DescribeNegationTo(::std::ostream * os)673 void DescribeNegationTo(::std::ostream* os) const {
674 *os << "is not a fully initialized protocol buffer";
675 }
676
677 template <typename T>
MatchAndExplain(T & arg,::testing::MatchResultListener * listener)678 bool MatchAndExplain(T& arg, // NOLINT
679 ::testing::MatchResultListener* listener) const {
680 if (!arg.IsInitialized()) {
681 *listener << "which is missing the following required fields: "
682 << arg.InitializationErrorString();
683 return false;
684 }
685 return true;
686 }
687
688 // It's critical for this overload to take a T* instead of a const
689 // T*. Otherwise the other version would be a better match when arg
690 // is a pointer to a non-const value.
691 template <typename T>
MatchAndExplain(T * arg,::testing::MatchResultListener * listener)692 bool MatchAndExplain(T* arg, ::testing::MatchResultListener* listener) const {
693 if (listener->IsInterested() && arg != NULL) {
694 *listener << PrintProtoPointee(arg);
695 }
696 if (arg == NULL) {
697 *listener << "which is null";
698 return false;
699 } else if (!arg->IsInitialized()) {
700 *listener << ", which is missing the following required fields: "
701 << arg->InitializationErrorString();
702 return false;
703 } else {
704 return true;
705 }
706 }
707 };
708
709 // Implements EqualsProto and EquivToProto for 2-tuple matchers.
710 class TupleProtoMatcher {
711 public:
TupleProtoMatcher(const ProtoComparison & comp)712 explicit TupleProtoMatcher(const ProtoComparison& comp) : comp_(new auto(comp)) {}
713
TupleProtoMatcher(const TupleProtoMatcher & other)714 TupleProtoMatcher(const TupleProtoMatcher& other) : comp_(new auto(*other.comp_)) {}
715 TupleProtoMatcher(TupleProtoMatcher&& other) = default;
716
717 template <typename T1, typename T2>
718 operator ::testing::Matcher<::testing::tuple<T1, T2>>() const {
719 return MakeMatcher(new Impl<::testing::tuple<T1, T2>>(*comp_));
720 }
721 template <typename T1, typename T2>
722 operator ::testing::Matcher<const ::testing::tuple<T1, T2>&>() const {
723 return MakeMatcher(new Impl<const ::testing::tuple<T1, T2>&>(*comp_));
724 }
725
726 // Allows matcher transformers, e.g., Approximately(), Partially(), etc. to
727 // change the behavior of this 2-tuple matcher.
mutable_impl()728 TupleProtoMatcher& mutable_impl() { return *this; }
729
730 // Makes this matcher compare floating-points approximately.
SetCompareApproximately()731 void SetCompareApproximately() { comp_->float_comp = kProtoApproximate; }
732
733 // Makes this matcher treating NaNs as equal when comparing floating-points.
SetCompareTreatingNaNsAsEqual()734 void SetCompareTreatingNaNsAsEqual() { comp_->treating_nan_as_equal = true; }
735
736 // Makes this matcher ignore string elements specified by their fully
737 // qualified names, i.e., names corresponding to
738 // FieldDescriptor.full_name().
739 template <class Iterator>
AddCompareIgnoringFields(Iterator first,Iterator last)740 void AddCompareIgnoringFields(Iterator first, Iterator last) {
741 comp_->ignore_fields.insert(comp_->ignore_fields.end(), first, last);
742 }
743
744 // Makes this matcher compare repeated fields ignoring ordering of elements.
SetCompareRepeatedFieldsIgnoringOrdering()745 void SetCompareRepeatedFieldsIgnoringOrdering() {
746 comp_->repeated_field_comp = kProtoCompareRepeatedFieldsIgnoringOrdering;
747 }
748
749 // Sets the margin of error for approximate floating point comparison.
SetMargin(double margin)750 void SetMargin(double margin) {
751 // DCHECK(margin >= 0.0) << "Using a negative margin for Approximately";
752 comp_->has_custom_margin = true;
753 comp_->float_margin = margin;
754 }
755
756 // Sets the relative fraction of error for approximate floating point
757 // comparison.
SetFraction(double fraction)758 void SetFraction(double fraction) {
759 comp_->has_custom_fraction = true;
760 comp_->float_fraction = fraction;
761 }
762
763 // Makes this matcher compares protobufs partially.
SetComparePartially()764 void SetComparePartially() { comp_->scope = kProtoPartial; }
765
766 private:
767 template <typename Tuple>
768 class Impl : public ::testing::MatcherInterface<Tuple> {
769 public:
Impl(const ProtoComparison & comp)770 explicit Impl(const ProtoComparison& comp) : comp_(comp) {}
MatchAndExplain(Tuple args,::testing::MatchResultListener *)771 virtual bool MatchAndExplain(Tuple args,
772 ::testing::MatchResultListener* /* listener */) const {
773 using ::testing::get;
774 return ProtoCompare(comp_, get<0>(args), get<1>(args));
775 }
DescribeTo(::std::ostream * os)776 virtual void DescribeTo(::std::ostream* os) const {
777 *os << (comp_.field_comp == kProtoEqual ? "are equal" : "are equivalent");
778 }
DescribeNegationTo(::std::ostream * os)779 virtual void DescribeNegationTo(::std::ostream* os) const {
780 *os << (comp_.field_comp == kProtoEqual ? "are not equal" : "are not equivalent");
781 }
782
783 private:
784 const ProtoComparison comp_;
785 };
786
787 std::unique_ptr<ProtoComparison> comp_;
788 };
789
790 } // namespace internal
791
792 // Creates a polymorphic matcher that matches a 2-tuple where
793 // first.Equals(second) is true.
EqualsProto()794 inline internal::TupleProtoMatcher EqualsProto() {
795 internal::ProtoComparison comp;
796 comp.field_comp = internal::kProtoEqual;
797 return internal::TupleProtoMatcher(comp);
798 }
799
800 // Creates a polymorphic matcher that matches a 2-tuple where
801 // first.Equivalent(second) is true.
EquivToProto()802 inline internal::TupleProtoMatcher EquivToProto() {
803 internal::ProtoComparison comp;
804 comp.field_comp = internal::kProtoEquiv;
805 return internal::TupleProtoMatcher(comp);
806 }
807
808 // Constructs a matcher that matches the argument if
809 // argument.Equals(x) or argument->Equals(x) returns true.
EqualsProto(const google::protobuf::Message & x)810 inline internal::PolymorphicProtoMatcher EqualsProto(const google::protobuf::Message& x) {
811 internal::ProtoComparison comp;
812 comp.field_comp = internal::kProtoEqual;
813 return ::testing::MakePolymorphicMatcher(
814 internal::ProtoMatcher(x, internal::kMayBeUninitialized, comp));
815 }
EqualsProto(const std::string & x)816 inline ::testing::PolymorphicMatcher<internal::ProtoStringMatcher> EqualsProto(
817 const std::string& x) {
818 internal::ProtoComparison comp;
819 comp.field_comp = internal::kProtoEqual;
820 return ::testing::MakePolymorphicMatcher(
821 internal::ProtoStringMatcher(x, internal::kMayBeUninitialized, comp));
822 }
823 template <class Proto>
EqualsProto(const std::string & str)824 inline internal::PolymorphicProtoMatcher EqualsProto(const std::string& str) {
825 return EqualsProto(internal::MakePartialProtoFromAscii<Proto>(str));
826 }
827
828 // Constructs a matcher that matches the argument if
829 // argument.Equivalent(x) or argument->Equivalent(x) returns true.
EquivToProto(const google::protobuf::Message & x)830 inline internal::PolymorphicProtoMatcher EquivToProto(const google::protobuf::Message& x) {
831 internal::ProtoComparison comp;
832 comp.field_comp = internal::kProtoEquiv;
833 return ::testing::MakePolymorphicMatcher(
834 internal::ProtoMatcher(x, internal::kMayBeUninitialized, comp));
835 }
EquivToProto(const std::string & x)836 inline ::testing::PolymorphicMatcher<internal::ProtoStringMatcher> EquivToProto(
837 const std::string& x) {
838 internal::ProtoComparison comp;
839 comp.field_comp = internal::kProtoEquiv;
840 return ::testing::MakePolymorphicMatcher(
841 internal::ProtoStringMatcher(x, internal::kMayBeUninitialized, comp));
842 }
843 template <class Proto>
EquivToProto(const std::string & str)844 inline internal::PolymorphicProtoMatcher EquivToProto(const std::string& str) {
845 return EquivToProto(internal::MakePartialProtoFromAscii<Proto>(str));
846 }
847
848 // Constructs a matcher that matches the argument if
849 // argument.IsInitialized() or argument->IsInitialized() returns true.
IsInitializedProto()850 inline ::testing::PolymorphicMatcher<internal::IsInitializedProtoMatcher> IsInitializedProto() {
851 return ::testing::MakePolymorphicMatcher(internal::IsInitializedProtoMatcher());
852 }
853
854 // Constructs a matcher that matches an argument whose IsInitialized()
855 // and Equals(x) methods both return true. The argument can be either
856 // a protocol buffer or a pointer to it.
EqualsInitializedProto(const google::protobuf::Message & x)857 inline internal::PolymorphicProtoMatcher EqualsInitializedProto(
858 const google::protobuf::Message& x) {
859 internal::ProtoComparison comp;
860 comp.field_comp = internal::kProtoEqual;
861 return ::testing::MakePolymorphicMatcher(
862 internal::ProtoMatcher(x, internal::kMustBeInitialized, comp));
863 }
EqualsInitializedProto(const std::string & x)864 inline ::testing::PolymorphicMatcher<internal::ProtoStringMatcher> EqualsInitializedProto(
865 const std::string& x) {
866 internal::ProtoComparison comp;
867 comp.field_comp = internal::kProtoEqual;
868 return ::testing::MakePolymorphicMatcher(
869 internal::ProtoStringMatcher(x, internal::kMustBeInitialized, comp));
870 }
871 template <class Proto>
EqualsInitializedProto(const std::string & str)872 inline internal::PolymorphicProtoMatcher EqualsInitializedProto(const std::string& str) {
873 return EqualsInitializedProto(internal::MakePartialProtoFromAscii<Proto>(str));
874 }
875
876 // Constructs a matcher that matches an argument whose IsInitialized()
877 // and Equivalent(x) methods both return true. The argument can be
878 // either a protocol buffer or a pointer to it.
EquivToInitializedProto(const google::protobuf::Message & x)879 inline internal::PolymorphicProtoMatcher EquivToInitializedProto(
880 const google::protobuf::Message& x) {
881 internal::ProtoComparison comp;
882 comp.field_comp = internal::kProtoEquiv;
883 return ::testing::MakePolymorphicMatcher(
884 internal::ProtoMatcher(x, internal::kMustBeInitialized, comp));
885 }
EquivToInitializedProto(const std::string & x)886 inline ::testing::PolymorphicMatcher<internal::ProtoStringMatcher> EquivToInitializedProto(
887 const std::string& x) {
888 internal::ProtoComparison comp;
889 comp.field_comp = internal::kProtoEquiv;
890 return ::testing::MakePolymorphicMatcher(
891 internal::ProtoStringMatcher(x, internal::kMustBeInitialized, comp));
892 }
893 template <class Proto>
EquivToInitializedProto(const std::string & str)894 inline internal::PolymorphicProtoMatcher EquivToInitializedProto(const std::string& str) {
895 return EquivToInitializedProto(internal::MakePartialProtoFromAscii<Proto>(str));
896 }
897
898 namespace proto {
899
900 // Approximately(m) returns a matcher that is the same as m, except
901 // that it compares floating-point fields approximately (using
902 // google::protobuf::util::MessageDifferencer's APPROXIMATE comparison option).
903 // The inner matcher m can be any of the Equals* and EquivTo* protobuf
904 // matchers above.
905 template <class InnerProtoMatcher>
Approximately(InnerProtoMatcher inner_proto_matcher)906 inline InnerProtoMatcher Approximately(InnerProtoMatcher inner_proto_matcher) {
907 inner_proto_matcher.mutable_impl().SetCompareApproximately();
908 return inner_proto_matcher;
909 }
910
911 // Alternative version of Approximately which takes an explicit margin of error.
912 template <class InnerProtoMatcher>
Approximately(InnerProtoMatcher inner_proto_matcher,double margin)913 inline InnerProtoMatcher Approximately(InnerProtoMatcher inner_proto_matcher, double margin) {
914 inner_proto_matcher.mutable_impl().SetCompareApproximately();
915 inner_proto_matcher.mutable_impl().SetMargin(margin);
916 return inner_proto_matcher;
917 }
918
919 // Alternative version of Approximately which takes an explicit margin of error
920 // and a relative fraction of error and will match if either is satisfied.
921 template <class InnerProtoMatcher>
Approximately(InnerProtoMatcher inner_proto_matcher,double margin,double fraction)922 inline InnerProtoMatcher Approximately(InnerProtoMatcher inner_proto_matcher, double margin,
923 double fraction) {
924 inner_proto_matcher.mutable_impl().SetCompareApproximately();
925 inner_proto_matcher.mutable_impl().SetMargin(margin);
926 inner_proto_matcher.mutable_impl().SetFraction(fraction);
927 return inner_proto_matcher;
928 }
929
930 // TreatingNaNsAsEqual(m) returns a matcher that is the same as m, except that
931 // it compares floating-point fields such that NaNs are equal.
932 // The inner matcher m can be any of the Equals* and EquivTo* protobuf matchers
933 // above.
934 template <class InnerProtoMatcher>
TreatingNaNsAsEqual(InnerProtoMatcher inner_proto_matcher)935 inline InnerProtoMatcher TreatingNaNsAsEqual(InnerProtoMatcher inner_proto_matcher) {
936 inner_proto_matcher.mutable_impl().SetCompareTreatingNaNsAsEqual();
937 return inner_proto_matcher;
938 }
939
940 // IgnoringFields(fields, m) returns a matcher that is the same as m, except the
941 // specified fields will be ignored when matching
942 // (using google::protobuf::util::MessageDifferencer::IgnoreField). Each element
943 // in fields are specified by their fully qualified names, i.e., the names
944 // corresponding to FieldDescriptor.full_name(). (e.g.
945 // testing.internal.FooProto2.member). m can be any of the Equals* and EquivTo*
946 // protobuf matchers above. It can also be any of the transformer matchers
947 // listed here (e.g. Approximately, TreatingNaNsAsEqual) as long as the intent
948 // of the each concatenated matcher is mutually exclusive (e.g. using
949 // IgnoringFields in conjunction with Partially can have different results
950 // depending on whether the fields specified in IgnoringFields is part of the
951 // fields covered by Partially).
952 template <class InnerProtoMatcher, class Container>
IgnoringFields(const Container & ignore_fields,InnerProtoMatcher inner_proto_matcher)953 inline InnerProtoMatcher IgnoringFields(const Container& ignore_fields,
954 InnerProtoMatcher inner_proto_matcher) {
955 inner_proto_matcher.mutable_impl().AddCompareIgnoringFields(ignore_fields.begin(),
956 ignore_fields.end());
957 return inner_proto_matcher;
958 }
959
960 #ifdef LANG_CXX11
961 template <class InnerProtoMatcher, class T>
IgnoringFields(std::initializer_list<T> il,InnerProtoMatcher inner_proto_matcher)962 inline InnerProtoMatcher IgnoringFields(std::initializer_list<T> il,
963 InnerProtoMatcher inner_proto_matcher) {
964 inner_proto_matcher.mutable_impl().AddCompareIgnoringFields(il.begin(), il.end());
965 return inner_proto_matcher;
966 }
967 #endif // LANG_CXX11
968
969 // IgnoringRepeatedFieldOrdering(m) returns a matcher that is the same as m,
970 // except that it ignores the relative ordering of elements within each repeated
971 // field in m. See google::protobuf::MessageDifferencer::TreatAsSet() for more
972 // details.
973 template <class InnerProtoMatcher>
IgnoringRepeatedFieldOrdering(InnerProtoMatcher inner_proto_matcher)974 inline InnerProtoMatcher IgnoringRepeatedFieldOrdering(InnerProtoMatcher inner_proto_matcher) {
975 inner_proto_matcher.mutable_impl().SetCompareRepeatedFieldsIgnoringOrdering();
976 return inner_proto_matcher;
977 }
978
979 // Partially(m) returns a matcher that is the same as m, except that
980 // only fields present in the expected protobuf are considered (using
981 // google::protobuf::util::MessageDifferencer's PARTIAL comparison option). For
982 // example, Partially(EqualsProto(p)) will ignore any field that's
983 // not set in p when comparing the protobufs. The inner matcher m can
984 // be any of the Equals* and EquivTo* protobuf matchers above.
985 template <class InnerProtoMatcher>
Partially(InnerProtoMatcher inner_proto_matcher)986 inline InnerProtoMatcher Partially(InnerProtoMatcher inner_proto_matcher) {
987 inner_proto_matcher.mutable_impl().SetComparePartially();
988 return inner_proto_matcher;
989 }
990
991 // WhenDeserialized(m) is a matcher that matches a string that can be
992 // deserialized as a protobuf that matches m. m must be a protobuf
993 // matcher where the expected protobuf type is known at run time.
WhenDeserialized(const internal::PolymorphicProtoMatcher & proto_matcher)994 inline ::testing::PolymorphicMatcher<internal::WhenDeserializedMatcher> WhenDeserialized(
995 const internal::PolymorphicProtoMatcher& proto_matcher) {
996 return ::testing::MakePolymorphicMatcher(internal::WhenDeserializedMatcher(proto_matcher));
997 }
998
999 // WhenDeserializedAs<Proto>(m) is a matcher that matches a string
1000 // that can be deserialized as a protobuf of type Proto that matches
1001 // m, which can be any valid protobuf matcher.
1002 template <class Proto, class InnerMatcher>
WhenDeserializedAs(const InnerMatcher & inner_matcher)1003 inline ::testing::PolymorphicMatcher<internal::WhenDeserializedAsMatcher<Proto>> WhenDeserializedAs(
1004 const InnerMatcher& inner_matcher) {
1005 return MakePolymorphicMatcher(internal::WhenDeserializedAsMatcher<Proto>(
1006 ::testing::SafeMatcherCast<const Proto&>(inner_matcher)));
1007 }
1008
1009 } // namespace proto
1010 } // namespace android
1011