| // Protocol Buffers - Google's data interchange format | |
| // Copyright 2026 Google LLC. All rights reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file or at | |
| // https://developers.google.com/open-source/licenses/bsd | |
| #include "conformance/matchers.h" | |
| #include <cstddef> | |
| #include <memory> | |
| #include <ostream> | |
| #include <string> | |
| #include <utility> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include "absl/base/nullability.h" | |
| #include "absl/base/thread_annotations.h" | |
| #include "absl/log/absl_check.h" | |
| #include "absl/log/absl_log.h" | |
| #include "absl/memory/memory.h" | |
| #include "absl/status/status.h" | |
| #include "absl/strings/ascii.h" | |
| #include "absl/strings/escaping.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/str_format.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/synchronization/mutex.h" | |
| #include "absl/types/optional.h" | |
| #include "conformance/binary_wireformat.h" | |
| #include "conformance/conformance.pb.h" | |
| #include "conformance/global_test_environment.h" | |
| #include "conformance/test_manager.h" | |
| #include "conformance/testee.h" | |
| #include "google/protobuf/descriptor.h" | |
| #include "google/protobuf/message.h" | |
| #include "google/protobuf/port.h" | |
| #include "google/protobuf/text_format.h" | |
| #include "google/protobuf/util/field_comparator.h" | |
| #include "google/protobuf/util/message_differencer.h" | |
| namespace google { | |
| namespace protobuf { | |
| namespace conformance { | |
| namespace { | |
| using ::conformance::ConformanceResponse; | |
| using ::conformance::WireFormat; | |
| using ::google::protobuf::conformance::internal::GetGlobalTestManager; | |
| using ::google::protobuf::conformance::internal::TestManager; | |
| using ::google::protobuf::conformance::internal::TestResult; | |
| // Implements EqualsTextProto() and EqualsBinaryProto(): matches a message | |
| // equivalent to the one obtained by decoding `expected` (in `format`) as the | |
| // actual message's type. | |
| class EquivalentMessageMatcher { | |
| public: | |
| using is_gtest_matcher = void; | |
| EquivalentMessageMatcher(WireFormat format, std::string expected) | |
| : format_(format), expected_(std::move(expected)) { | |
| ABSL_CHECK(format == ::conformance::PROTOBUF || | |
| format == ::conformance::TEXT_FORMAT) | |
| << "Unsupported expected message format " << WireFormat_Name(format); | |
| } | |
| bool MatchAndExplain(const Message& actual, | |
| testing::MatchResultListener* listener) const { | |
| std::unique_ptr<Message> expected = absl::WrapUnique(actual.New()); | |
| if (format_ == ::conformance::PROTOBUF) { | |
| ABSL_CHECK(expected->ParseFromString(expected_)) | |
| << "Failed to parse expected wire data for " | |
| << actual.GetDescriptor()->full_name() << ": " | |
| << absl::CEscape(expected_); | |
| } else { | |
| ABSL_CHECK(TextFormat::ParseFromString(expected_, expected.get())) | |
| << "Failed to parse expected text proto for " | |
| << actual.GetDescriptor()->full_name() << ": " << expected_; | |
| } | |
| util::MessageDifferencer differencer; | |
| util::DefaultFieldComparator field_comparator; | |
| field_comparator.set_treat_nan_as_equal(true); | |
| differencer.set_field_comparator(&field_comparator); | |
| std::string differences; | |
| differencer.ReportDifferencesToString(&differences); | |
| if (differencer.Compare(*expected, actual)) { | |
| return true; | |
| } | |
| *listener << "Output was not equivalent to reference message: " | |
| << differences; | |
| return false; | |
| } | |
| void DescribeTo(std::ostream* os) const { | |
| *os << "equals " | |
| << (format_ == ::conformance::PROTOBUF ? "binary proto" : "text proto") | |
| << " \"" << absl::CEscape(expected_) << "\""; | |
| } | |
| void DescribeNegationTo(std::ostream* os) const { | |
| *os << "doesn't equal " | |
| << (format_ == ::conformance::PROTOBUF ? "binary proto" : "text proto") | |
| << " \"" << absl::CEscape(expected_) << "\""; | |
| } | |
| private: | |
| WireFormat format_; | |
| std::string expected_; | |
| }; | |
| // Matches a result whose response holds a specific kind of error. | |
| class FailureMatcher { | |
| public: | |
| using is_gtest_matcher = void; | |
| // `name` is used for descriptions (e.g. "parse error"). `failure_message` | |
| // is the explanation when the response isn't the expected error, and | |
| // `runtime_error_failure_message` the one when it is a runtime error. | |
| FailureMatcher(absl::string_view name, | |
| ConformanceResponse::ResultCase expected_result, | |
| absl::string_view failure_message, | |
| absl::string_view runtime_error_failure_message) | |
| : name_(name), | |
| expected_result_(expected_result), | |
| failure_message_(failure_message), | |
| runtime_error_failure_message_(runtime_error_failure_message) {} | |
| bool MatchAndExplain(const TestResult& result, | |
| testing::MatchResultListener* listener) const { | |
| ConformanceResponse::ResultCase actual = result.response().result_case(); | |
| if (actual == expected_result_) { | |
| return true; | |
| } | |
| if (actual == ConformanceResponse::kSkipped) { | |
| *listener << "the testee skipped the test: " | |
| << result.response().skipped(); | |
| return false; | |
| } | |
| if (actual == ConformanceResponse::kRuntimeError) { | |
| *listener << runtime_error_failure_message_; | |
| return false; | |
| } | |
| *listener << failure_message_; | |
| return false; | |
| } | |
| void DescribeTo(std::ostream* os) const { *os << "is a " << name_; } | |
| void DescribeNegationTo(std::ostream* os) const { | |
| *os << "is not a " << name_; | |
| } | |
| private: | |
| std::string name_; | |
| ConformanceResponse::ResultCase expected_result_; | |
| std::string failure_message_; | |
| std::string runtime_error_failure_message_; | |
| }; | |
| // Truncates a payload for debug output, exactly like the legacy runner. | |
| void TruncateDebugPayload(std::string& payload) { | |
| constexpr size_t kMaxDebugPayloadSize = 200; | |
| if (payload.size() > kMaxDebugPayloadSize) { | |
| payload.resize(kMaxDebugPayloadSize); | |
| payload.append("...(truncated)"); | |
| } | |
| } | |
| // Returns a copy of `response` whose payload is truncated for debug output. | |
| ConformanceResponse TruncateResponse(const ConformanceResponse& response) { | |
| ConformanceResponse debug_response(response); | |
| switch (debug_response.result_case()) { | |
| case ConformanceResponse::kProtobufPayload: | |
| TruncateDebugPayload(*debug_response.mutable_protobuf_payload()); | |
| break; | |
| case ConformanceResponse::kJsonPayload: | |
| TruncateDebugPayload(*debug_response.mutable_json_payload()); | |
| break; | |
| case ConformanceResponse::kTextPayload: | |
| TruncateDebugPayload(*debug_response.mutable_text_payload()); | |
| break; | |
| case ConformanceResponse::kJspbPayload: | |
| TruncateDebugPayload(*debug_response.mutable_jspb_payload()); | |
| break; | |
| default: | |
| break; | |
| } | |
| return debug_response; | |
| } | |
| // Creates an empty message of the given type, which must be a generated one. | |
| std::unique_ptr<Message> NewMessage(const Descriptor* type) { | |
| const Message* prototype = | |
| MessageFactory::generated_factory()->GetPrototype(type); | |
| ABSL_CHECK(prototype != nullptr) | |
| << "Not a generated message type: " << type->full_name(); | |
| return absl::WrapUnique(prototype->New()); | |
| } | |
| // Prints a message on a single line (expanding Any, short repeated | |
| // primitives) for failure output. An explicit printer keeps the output | |
| // stable, unlike DebugString()'s. | |
| std::string ToShortString(const Message& message) { | |
| TextFormat::Printer printer; | |
| printer.SetSingleLineMode(true); | |
| printer.SetExpandAny(true); | |
| printer.SetUseShortRepeatedPrimitives(true); | |
| std::string text; | |
| ABSL_CHECK(printer.PrintToString(message, &text)); | |
| absl::StripTrailingAsciiWhitespace(&text); | |
| return text; | |
| } | |
| // The response field that a successful test with the given output format is | |
| // expected to populate. Tests always ask for a concrete format. | |
| ConformanceResponse::ResultCase ExpectedResultCase(WireFormat format) { | |
| switch (format) { | |
| case ::conformance::PROTOBUF: | |
| return ConformanceResponse::kProtobufPayload; | |
| case ::conformance::JSON: | |
| return ConformanceResponse::kJsonPayload; | |
| case ::conformance::TEXT_FORMAT: | |
| return ConformanceResponse::kTextPayload; | |
| case ::conformance::JSPB: | |
| return ConformanceResponse::kJspbPayload; | |
| case ::conformance::UNSPECIFIED: | |
| default: | |
| google::protobuf::internal::Unreachable(); | |
| } | |
| } | |
| // The name of the format of a payload, as used in legacy failure messages. | |
| absl::string_view PayloadFormatName(ConformanceResponse::ResultCase result) { | |
| switch (result) { | |
| case ConformanceResponse::kProtobufPayload: | |
| return "PROTOBUF"; | |
| case ConformanceResponse::kJsonPayload: | |
| return "JSON"; | |
| case ConformanceResponse::kTextPayload: | |
| return "TEXT_FORMAT"; | |
| case ConformanceResponse::kJspbPayload: | |
| return "JSPB"; | |
| default: | |
| google::protobuf::internal::Unreachable(); | |
| } | |
| } | |
| // The raw payload of a response, whichever format it is in. | |
| absl::string_view RawPayload(const ConformanceResponse& response) { | |
| switch (response.result_case()) { | |
| case ConformanceResponse::kProtobufPayload: | |
| return response.protobuf_payload(); | |
| case ConformanceResponse::kJsonPayload: | |
| return response.json_payload(); | |
| case ConformanceResponse::kTextPayload: | |
| return response.text_payload(); | |
| case ConformanceResponse::kJspbPayload: | |
| return response.jspb_payload(); | |
| default: | |
| google::protobuf::internal::Unreachable(); | |
| } | |
| } | |
| // The legacy failure message for a payload that couldn't be parsed. | |
| std::string UnparseableMessage(WireFormat format) { | |
| switch (format) { | |
| case ::conformance::PROTOBUF: | |
| return "Protobuf output we received from test was unparseable."; | |
| default: | |
| return absl::StrCat(WireFormat_Name(format), | |
| " output we received from test was unparseable."); | |
| } | |
| } | |
| // Parses the payload of `result` (according to its requested output format) | |
| // into a message of type `type`. Returns null and sets `failure_message` if | |
| // the payload is invalid or the format isn't supported. | |
| std::unique_ptr<Message> ParsePayload(const TestResult& result, | |
| const Descriptor* type, | |
| std::string& failure_message) { | |
| std::unique_ptr<Message> message = NewMessage(type); | |
| switch (result.format()) { | |
| case ::conformance::PROTOBUF: | |
| if (!message->ParseFromString(result.response().protobuf_payload())) { | |
| failure_message = UnparseableMessage(result.format()); | |
| return nullptr; | |
| } | |
| return message; | |
| case ::conformance::TEXT_FORMAT: { | |
| TextFormat::Parser parser; | |
| // Testees asked to print unknown fields emit them by field number, and | |
| // a known field named by number is the same field, so always accept | |
| // them. Unknown numbers still fail to parse. | |
| parser.AllowFieldNumber(true); | |
| if (!parser.ParseFromString(result.response().text_payload(), | |
| message.get())) { | |
| failure_message = UnparseableMessage(result.format()); | |
| return nullptr; | |
| } | |
| return message; | |
| } | |
| case ::conformance::JSON: | |
| case ::conformance::JSPB: | |
| case ::conformance::UNSPECIFIED: | |
| default: | |
| // TODO: b/410122158 - Support JSON once the JSON suite is migrated. | |
| failure_message = absl::StrCat( | |
| "WhenParsed is not supported for ", WireFormat_Name(result.format()), | |
| " output; use RawPayload() to match the raw JSON text until JSON " | |
| "matching is migrated to gtest (b/410122158)."); | |
| return nullptr; | |
| } | |
| } | |
| // Formats binary data the same way the legacy runner does in failure messages. | |
| std::string ToOctString(absl::string_view binary_string) { | |
| std::string oct_string; | |
| for (char ch : binary_string) { | |
| absl::StrAppendFormat(&oct_string, "\\%03o", ch); | |
| } | |
| return oct_string; | |
| } | |
| // Base class for the matchers that look at the payload of a successful | |
| // response. It handles all of the cases shared by every payload comparison | |
| // (no payload, an error response, the wrong output format, a skipped test) and | |
| // defers the actual comparison to MatchPayload(). | |
| class PayloadMatcher { | |
| public: | |
| using is_gtest_matcher = void; | |
| virtual ~PayloadMatcher() = default; | |
| bool MatchAndExplain(const TestResult& result, | |
| testing::MatchResultListener* listener) const; | |
| void DescribeTo(std::ostream* os) const { | |
| DescribeInnerTo(os, /*negation=*/false); | |
| } | |
| void DescribeNegationTo(std::ostream* os) const { | |
| DescribeInnerTo(os, /*negation=*/true); | |
| } | |
| protected: | |
| PayloadMatcher() = default; | |
| PayloadMatcher(const PayloadMatcher&) = default; | |
| PayloadMatcher& operator=(const PayloadMatcher&) = default; | |
| // Compares the payload of `result`, which is guaranteed to hold a payload of | |
| // the format the test requested. Returns true on a match; otherwise the | |
| // explanation written to `listener` becomes the failure message recorded in | |
| // the failure list. | |
| virtual bool MatchPayload(const TestResult& result, | |
| testing::MatchResultListener* listener) const = 0; | |
| // Describes the matcher (or its negation). | |
| virtual void DescribeInnerTo(std::ostream* os, bool negation) const = 0; | |
| }; | |
| bool PayloadMatcher::MatchAndExplain( | |
| const TestResult& result, testing::MatchResultListener* listener) const { | |
| const ConformanceResponse& response = result.response(); | |
| switch (response.result_case()) { | |
| case ConformanceResponse::RESULT_NOT_SET: | |
| *listener << "Response didn't have any field in the Response."; | |
| return false; | |
| case ConformanceResponse::kParseError: | |
| case ConformanceResponse::kTimeoutError: | |
| case ConformanceResponse::kRuntimeError: | |
| case ConformanceResponse::kSerializeError: | |
| *listener << "Failed to parse input or produce output."; | |
| return false; | |
| case ConformanceResponse::kSkipped: | |
| *listener << "the testee skipped the test: " << response.skipped(); | |
| return false; | |
| default: | |
| break; | |
| } | |
| if (response.result_case() != ExpectedResultCase(result.format())) { | |
| *listener << "Test was asked for " << WireFormat_Name(result.format()) | |
| << " output but provided " | |
| << PayloadFormatName(response.result_case()) << " instead."; | |
| return false; | |
| } | |
| return MatchPayload(result, listener); | |
| } | |
| // Implements WhenParsed() and WhenParsedAs(). | |
| class WhenParsedMatcher : public PayloadMatcher { | |
| public: | |
| // The payload is decoded as `type_override` if it is non-null, and as the | |
| // test's message type otherwise. | |
| explicit WhenParsedMatcher(testing::Matcher<const Message&> matcher, | |
| const Descriptor* type_override = nullptr) | |
| : matcher_(std::move(matcher)), type_override_(type_override) {} | |
| private: | |
| bool MatchPayload(const TestResult& result, | |
| testing::MatchResultListener* listener) const override; | |
| void DescribeInnerTo(std::ostream* os, bool negation) const override; | |
| testing::Matcher<const Message&> matcher_; | |
| // Null means "the test's message type". | |
| const Descriptor* absl_nullable type_override_; | |
| }; | |
| bool WhenParsedMatcher::MatchPayload( | |
| const TestResult& result, testing::MatchResultListener* listener) const { | |
| std::string failure_message; | |
| std::unique_ptr<Message> actual = ParsePayload( | |
| result, type_override_ != nullptr ? type_override_ : result.type(), | |
| failure_message); | |
| if (actual == nullptr) { | |
| *listener << failure_message; | |
| return false; | |
| } | |
| testing::StringMatchResultListener inner_listener; | |
| if (matcher_.MatchAndExplain(*actual, &inner_listener)) { | |
| *listener << inner_listener.str(); | |
| return true; | |
| } | |
| if (inner_listener.str().empty()) { | |
| *listener << "Expect: when parsed, " | |
| << testing::DescribeMatcher<const Message&>(matcher_) | |
| << ", but got: {" << ToShortString(*actual) << "}"; | |
| } else { | |
| *listener << inner_listener.str(); | |
| } | |
| return false; | |
| } | |
| void WhenParsedMatcher::DescribeInnerTo(std::ostream* os, bool negation) const { | |
| *os << "when parsed"; | |
| if (type_override_ != nullptr) { | |
| *os << " as " << type_override_->full_name(); | |
| } | |
| *os << ", " << testing::DescribeMatcher<const Message&>(matcher_, negation); | |
| } | |
| // Implements RawPayload(). | |
| class RawPayloadMatcher : public PayloadMatcher { | |
| public: | |
| explicit RawPayloadMatcher(std::string expected) | |
| : expected_(std::move(expected)) {} | |
| private: | |
| bool MatchPayload(const TestResult& result, | |
| testing::MatchResultListener* listener) const override; | |
| void DescribeInnerTo(std::ostream* os, bool negation) const override { | |
| *os << "payload " << (negation ? "isn't" : "is") << " equal to \"" | |
| << absl::CEscape(expected_) << "\""; | |
| } | |
| std::string expected_; | |
| }; | |
| bool RawPayloadMatcher::MatchPayload( | |
| const TestResult& result, testing::MatchResultListener* listener) const { | |
| // Like the legacy runner's `require_same_wire_format`, binary output must at | |
| // least be parseable as the test's message type. | |
| if (result.format() == ::conformance::PROTOBUF && | |
| !NewMessage(result.type()) | |
| ->ParseFromString(result.response().protobuf_payload())) { | |
| *listener << UnparseableMessage(result.format()); | |
| return false; | |
| } | |
| absl::string_view actual = RawPayload(result.response()); | |
| if (actual == expected_) { | |
| return true; | |
| } | |
| // TODO: b/568362905 - Point at the first differing byte instead of dumping | |
| // both payloads, once the legacy runner this message mirrors is gone. | |
| *listener << "Output was not equivalent to reference message: Expect: " | |
| << ToOctString(expected_) << ", but got: " << ToOctString(actual); | |
| return false; | |
| } | |
| // Implements Yields(). This is the single matcher that talks to the global | |
| // TestManager; see matchers.h for the policy it applies. | |
| // | |
| // The TestManager must only hear about each test once. It remembers the | |
| // names it was told (TestManager::WasReported()), and a result it already | |
| // knows is not evaluated again. gtest evaluates a failing matcher a second | |
| // time, through the same matcher object, to explain the failure. The matcher | |
| // keeps its last failed verdict for that rerun. Anything else that checks a | |
| // reported result is a test bug and fails. | |
| // | |
| // Matchers are nominally immutable. The cache only affects how a failure | |
| // that has already been decided is explained, never a verdict, and copies of | |
| // a Matcher share one impl, so the matcher behaves the same through every | |
| // copy. | |
| class YieldsMatcherImpl : public testing::MatcherInterface<const TestResult&> { | |
| public: | |
| explicit YieldsMatcherImpl(testing::Matcher<const TestResult&> inner) | |
| : inner_(std::move(inner)) {} | |
| bool MatchAndExplain(const TestResult& result, | |
| testing::MatchResultListener* listener) const override { | |
| { | |
| absl::MutexLock lock(&mutex_); | |
| if (last_failure_.has_value() && last_failure_->result == &result && | |
| last_failure_->name == result.name()) { | |
| *listener << last_failure_->explanation; | |
| return false; | |
| } | |
| } | |
| if (GetGlobalTestManager().WasReported(result.name())) { | |
| *listener << "TestResult for " << result.name() | |
| << " was already checked; each result may be checked exactly " | |
| "once"; | |
| return false; | |
| } | |
| absl::Status status = Evaluate(result); | |
| *listener << status.message(); | |
| absl::MutexLock lock(&mutex_); | |
| if (status.ok()) { | |
| last_failure_.reset(); | |
| } else { | |
| last_failure_ = LastFailure{&result, std::string(result.name()), | |
| std::string(status.message())}; | |
| } | |
| return status.ok(); | |
| } | |
| void DescribeTo(std::ostream* os) const override { | |
| *os << "yields a result that "; | |
| inner_.DescribeTo(os); | |
| *os << " (or is an expected failure)"; | |
| } | |
| void DescribeNegationTo(std::ostream* os) const override { | |
| *os << "doesn't yield a result that "; | |
| inner_.DescribeTo(os); | |
| *os << ", nor an expected failure"; | |
| } | |
| private: | |
| // Reports `result` to the global TestManager and returns the gtest verdict: | |
| // OK if the test passes, otherwise an error whose message explains why not. | |
| absl::Status Evaluate(const TestResult& result) const { | |
| TestManager& manager = GetGlobalTestManager(); | |
| const std::string name(result.name()); | |
| const ConformanceResponse& response = result.response(); | |
| // A skip is decided here; the inner matcher never sees it. The manager | |
| // marks a listed entry as seen and returns an error for it, so this is the | |
| // only place that reports a listed test the testee skipped. | |
| if (response.result_case() == ConformanceResponse::kSkipped) { | |
| absl::Status status = manager.ReportSkip(name, response.skipped()); | |
| ABSL_LOG(INFO) << "Skipping test " << name << ": " << response.skipped(); | |
| return status; | |
| } | |
| testing::StringMatchResultListener inner_listener; | |
| bool matched = inner_.MatchAndExplain(result, &inner_listener); | |
| std::string message = inner_listener.str(); | |
| if (matched) { | |
| return manager.ReportSuccess(name); | |
| } | |
| if (message.empty()) { | |
| message = absl::StrCat( | |
| "which doesn't match (", | |
| testing::DescribeMatcher<const TestResult&>(inner_), ")"); | |
| } | |
| // The manager decides whether the failure is expected (listed), tolerated | |
| // (above the enforcement level) or unexpected, and logs the first two. | |
| absl::Status status = | |
| manager.ReportFailure(name, result.priority(), message); | |
| if (status.ok()) { | |
| return status; | |
| } | |
| return absl::Status(status.code(), | |
| absl::StrCat(message, "\n", status.message())); | |
| } | |
| // The failure this matcher explained last, identified by the result's | |
| // address and name, so that gtest's rerun gets the same explanation. A | |
| // destroyed result's address may be reused, but not its name: Testee::Run() | |
| // rejects duplicate test names. | |
| struct LastFailure { | |
| const TestResult* result; | |
| std::string name; | |
| std::string explanation; | |
| }; | |
| testing::Matcher<const TestResult&> inner_; | |
| mutable absl::Mutex mutex_; | |
| mutable absl::optional<LastFailure> last_failure_ ABSL_GUARDED_BY(mutex_); | |
| }; | |
| } // namespace | |
| namespace internal { | |
| void PrintTo(const TestResult& result, std::ostream* absl_nonnull os) { | |
| *os << PriorityLevelName(result.priority()) << " test \"" << result.name() | |
| << "\" with response {" | |
| << ToShortString(TruncateResponse(result.response())) << "}"; | |
| // Binary payloads are opaque, so also show what they decode to. | |
| if (result.response().has_protobuf_payload()) { | |
| std::unique_ptr<Message> decoded = NewMessage(result.type()); | |
| if (decoded->ParseFromString(result.response().protobuf_payload())) { | |
| *os << " (decoded: {" << ToShortString(*decoded) << "})"; | |
| } else { | |
| *os << " (unparseable)"; | |
| } | |
| } | |
| } | |
| testing::Matcher<const TestResult&> MakeWhenParsedMatcher( | |
| testing::Matcher<const Message&> m, | |
| const Descriptor* absl_nullable type_override) { | |
| return WhenParsedMatcher(std::move(m), type_override); | |
| } | |
| testing::Matcher<const TestResult&> MakeYieldsMatcher( | |
| testing::Matcher<const TestResult&> inner) { | |
| return testing::MakeMatcher(new YieldsMatcherImpl(std::move(inner))); | |
| } | |
| } // namespace internal | |
| testing::Matcher<const internal::TestResult&> RawPayload(Wire bytes) { | |
| return RawPayloadMatcher(std::move(bytes).str()); | |
| } | |
| testing::Matcher<const Message&> EqualsTextProto(absl::string_view text) { | |
| return EquivalentMessageMatcher(::conformance::TEXT_FORMAT, | |
| std::string(text)); | |
| } | |
| testing::Matcher<const Message&> EqualsBinaryProto(Wire bytes) { | |
| return EquivalentMessageMatcher(::conformance::PROTOBUF, | |
| std::move(bytes).str()); | |
| } | |
| testing::Matcher<const internal::TestResult&> IsParseError() { | |
| return FailureMatcher( | |
| "parse error", ConformanceResponse::kParseError, | |
| "Should have failed to parse, but didn't.", | |
| "Should have failed to parse, but raised an error instead."); | |
| } | |
| testing::Matcher<const internal::TestResult&> IsSerializeError() { | |
| return FailureMatcher( | |
| "serialize error", ConformanceResponse::kSerializeError, | |
| "Should have failed to serialize, but didn't.", | |
| "Should have failed to serialize, but raised an error instead."); | |
| } | |
| } // namespace conformance | |
| } // namespace protobuf | |
| } // namespace google |