blob: 080d4bfa9d49c87d68b0cee032d6a525ba07ffad [file]
// 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
// gMock matchers for conformance TestResults.
//
// Every conformance test ends in one EXPECT_THAT. Its matcher is Yields()
// wrapped around one of the leaf matchers defined here:
//
// EXPECT_THAT(
// Testee()
// .ParseBinary(TestAllTypesProto2::descriptor(), input)
// .SerializeBinary(),
// Yields(WhenParsed(EqualsTextProto(R"pb(optional_int32: 1)pb"))));
//
// The leaf matchers (WhenParsed, RawPayload, IsParseError, ...) only look at
// the TestResult, so they compose with other gMock matchers:
// Yields(AnyOf(IsParseError(), WhenParsed(m))) works as expected.
// EqualsTextProto() and EqualsBinaryProto() match a `const Message&` and are
// meant to be used inside WhenParsed().
//
// Yields() is the one matcher that talks to the global TestManager. It
// records the outcome of the test and turns the failure list into the gtest
// verdict:
//
// - A failure that is in the failure list passes. A listed test that
// succeeds fails.
// - A failure of a test above the enforcement level is tolerated unless
// the test is listed. kP0 failures always count (see TestPriority).
// - A test the testee skipped passes unless it is listed. The TestManager
// counts it in ListedSkips().
//
// The failure messages of the leaf matchers end up in failure lists, so each
// matcher documents its message and keeps it stable. Messages that come from
// an inner matcher use gMock's wording, which can change between versions.
// Failure list entries for those should only use a prefix of the message (see
// TestManager::ReportFailure()).
//
// This file also defines PrintTo() for TestResult, which gtest uses to print a
// result when a matcher on it fails.
#ifndef GOOGLE_PROTOBUF_CONFORMANCE_MATCHERS_H__
#define GOOGLE_PROTOBUF_CONFORMANCE_MATCHERS_H__
#include <ostream>
#include <utility>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/base/nullability.h"
#include "absl/strings/string_view.h"
#include "conformance/binary_wireformat.h"
#include "conformance/testee.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/message.h"
namespace google {
namespace protobuf {
namespace conformance {
namespace internal {
// Prints a TestResult in gtest failure output. The output has the test's
// priority and name and a short form of the response. Long payloads are
// truncated. Binary payloads are also decoded as the test's message type.
// Declared in TestResult's namespace so that gtest finds it through ADL. It
// lives with the matchers because their failures are where results get
// printed.
void PrintTo(const TestResult& result, std::ostream* absl_nonnull os);
// Implements WhenParsed() and WhenParsedAs() below. The payload is
// decoded as `type_override` if it is non-null, and as the test's message type
// otherwise.
testing::Matcher<const TestResult&> MakeWhenParsedMatcher(
testing::Matcher<const Message&> m,
const Descriptor* absl_nullable type_override = nullptr);
// Implements Yields(): see the function below for the semantics.
testing::Matcher<const TestResult&> MakeYieldsMatcher(
testing::Matcher<const TestResult&> inner);
} // namespace internal
// Matches a result whose payload, decoded as the test's message type, matches
// `m`. The payload is decoded from binary or text output. `m` is any matcher
// on `const Message&`, usually EqualsTextProto() or EqualsBinaryProto().
//
// EXPECT_THAT(
// Testee().ParseBinary(type, input).SerializeBinary(),
// Yields(WhenParsed(EqualsTextProto(R"pb(optional_int32: 1)pb"))));
//
// A payload that can't be decoded fails with "<format> output we received
// from test was unparseable." Otherwise the failure message comes from `m`.
// JSON output can't be decoded yet (b/410122158) and fails with a message
// saying so. Use RawPayload() for it.
template <typename M>
testing::Matcher<const internal::TestResult&> WhenParsed(M m) {
return internal::MakeWhenParsedMatcher(
testing::SafeMatcherCast<const Message&>(std::move(m)));
}
// Like WhenParsed(), but decodes the payload as the generated type `T`
// instead of the message type the test was run against. Use it when the
// testee serializes unknown fields that a richer "shadow" type such as
// UnknownToTestAllTypes can decode:
//
// EXPECT_THAT(Testee("Foo").ParseBinary(type, input).SerializeBinary(),
// Yields(WhenParsedAs<UnknownToTestAllTypes>(
// EqualsBinaryProto(input))));
//
// Failure messages are the same as WhenParsed()'s.
template <typename T, typename M>
testing::Matcher<const internal::TestResult&> WhenParsedAs(M m) {
return internal::MakeWhenParsedMatcher(
testing::SafeMatcherCast<const Message&>(std::move(m)), T::descriptor());
}
// Matches a result whose raw payload is exactly `bytes`, whatever the output
// format.
//
// EXPECT_THAT(Testee().ParseBinary(type, input).SerializeBinary(),
// Yields(RawPayload(input)));
//
// A mismatch fails with "Output was not equivalent to reference message:
// Expect: <octal>, but got: <octal>". A response that isn't a payload of the
// requested format fails with a fixed message for that case. That covers no
// result, an error, a skipped test and the wrong output format. A PROTOBUF
// payload must also be parseable as the test's message type. Otherwise it
// fails with "Protobuf output we received from test was unparseable."
testing::Matcher<const internal::TestResult&> RawPayload(Wire bytes);
// Matches a message equivalent to `text`, parsed as the actual message's type.
// Messages are compared with MessageDifferencer, with NaN equal to NaN.
//
// EXPECT_THAT(
// Testee().ParseBinary(type, input).SerializeBinary(),
// Yields(WhenParsed(EqualsTextProto(R"pb(optional_int32: 1)pb"))));
testing::Matcher<const Message&> EqualsTextProto(absl::string_view text);
// Like EqualsTextProto(), but the expected message is the binary serialization
// `bytes`. It takes a Wire rather than a string so that NUL bytes survive.
// Messages are compared with MessageDifferencer, with NaN equal to NaN.
//
// EXPECT_THAT(Testee().ParseBinary(type, input).SerializeBinary(),
// Yields(WhenParsed(EqualsBinaryProto(VarintField(1, 1)))));
testing::Matcher<const Message&> EqualsBinaryProto(Wire bytes);
// TODO: b/410122158 - Add JSON matchers once JSON support is migrated.
// Matches a response that reports a parse error.
//
// EXPECT_THAT(Testee().ParseBinary(type, Wire("\x08")).SerializeBinary(),
// Yields(IsParseError()));
//
// Any other response fails with "Should have failed to parse, but didn't." A
// runtime error fails with "Should have failed to parse, but raised an error
// instead."
testing::Matcher<const internal::TestResult&> IsParseError();
// Matches a response that reports a serialize error.
//
// EXPECT_THAT(Testee().ParseBinary(type, input).SerializeJson(),
// Yields(IsSerializeError()));
//
// Any other response fails like it does for IsParseError(), with the
// corresponding "Should have failed to serialize, ..." messages.
testing::Matcher<const internal::TestResult&> IsSerializeError();
// Wraps the matcher of every conformance test's EXPECT_THAT.
//
// EXPECT_THAT(Testee().ParseBinary(type, input).SerializeBinary(),
// Yields(WhenParsed(EqualsBinaryProto(input))));
//
// Yields() evaluates `inner` against the TestResult, reports the outcome to
// the global TestManager and applies the failure list and priority rules
// described at the top of this file. Its verdict is not simply `inner`'s: an
// expected failure passes and an unexpected success fails. Do not wrap
// Yields() in Not() or other combinators. Compose `inner` instead.
//
// Each test may be checked exactly once. The first evaluation reports the
// result to the TestManager, which remembers the test's name. gtest
// evaluates a failing matcher a second time to explain the failure; Yields()
// replays its own failed verdict for that. Any other check of a result the
// TestManager has already heard about fails with "already checked".
template <typename M>
testing::Matcher<const internal::TestResult&> Yields(M inner) {
return internal::MakeYieldsMatcher(
testing::SafeMatcherCast<const internal::TestResult&>(std::move(inner)));
}
} // namespace conformance
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_CONFORMANCE_MATCHERS_H__