| // 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/test_fixture.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "google/protobuf/descriptor.pb.h" |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include "absl/log/absl_check.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/strings/strip.h" |
| #include "conformance/binary_wireformat.h" |
| #include "conformance/conformance.pb.h" |
| #include "conformance/matchers.h" |
| #include "conformance/mock_test_runner.h" |
| #include "conformance/test_environment.h" |
| #include "conformance/test_environment_testing.h" |
| #include "conformance/test_protos/test_messages_edition2023.pb.h" |
| #include "conformance/test_protos/test_messages_edition_unstable.pb.h" |
| #include "conformance/testee.h" |
| #include "google/protobuf/descriptor.h" |
| #include "google/protobuf/test_messages_proto2.pb.h" |
| #include "google/protobuf/test_messages_proto3.pb.h" |
| #include "google/protobuf/text_format.h" |
| |
| namespace google { |
| namespace protobuf { |
| namespace conformance { |
| namespace { |
| |
| using ::google::protobuf::conformance::internal::ConformanceEnvironmentOptions; |
| using ::protobuf_test_messages::edition_unstable::TestAllTypesEditionUnstable; |
| using ::protobuf_test_messages::editions::TestAllTypesEdition2023; |
| using ::protobuf_test_messages::proto2::TestAllTypesProto2; |
| using ::protobuf_test_messages::proto3::TestAllTypesProto3; |
| using ::testing::_; |
| using ::testing::NiceMock; |
| using ::testing::Return; |
| using ::testing::StartsWith; |
| |
| std::string SerializedResponse(absl::string_view textproto) { |
| ::conformance::ConformanceResponse response; |
| ABSL_CHECK(TextFormat::ParseFromString(textproto, &response)); |
| return response.SerializeAsString(); |
| } |
| |
| // A testee that successfully round-trips `optional_int32: 99` for every test |
| // (by default: being a mock, a test can still set expectations on it). |
| class FakeTestRunner : public NiceMock<MockTestRunner> { |
| public: |
| FakeTestRunner() { |
| ON_CALL(*this, RunTest) |
| .WillByDefault( |
| Return(SerializedResponse(R"pb(protobuf_payload: "\010c")pb"))); |
| } |
| }; |
| |
| // A ConformanceTest whose global environment is set up by the fixture itself, |
| // before SetUp() runs, so it can be exercised inside this test binary. |
| class FixtureTest : public ConformanceTest { |
| protected: |
| FixtureTest() : FixtureTest(ConformanceEnvironmentOptions()) {} |
| explicit FixtureTest(ConformanceEnvironmentOptions options) |
| : env_(WithRunner(std::move(options))) {} |
| |
| FakeTestRunner runner_; |
| |
| private: |
| ConformanceEnvironmentOptions WithRunner( |
| ConformanceEnvironmentOptions options) { |
| options.runner = &runner_; |
| return options; |
| } |
| |
| internal::ScopedGlobalConformanceEnvironment env_; |
| }; |
| |
| // --- Testee() ---------------------------------------------------------------- |
| |
| TEST_F(FixtureTest, InfersTestNameFromCurrentTest) { |
| EXPECT_CALL(runner_, RunTest("Required.Proto3.ProtobufInput." |
| "InfersTestNameFromCurrentTest.ProtobufOutput", |
| _)); |
| EXPECT_THAT( |
| Testee() |
| .ParseBinary(TestAllTypesProto3::descriptor(), VarintField(1, 99)) |
| .SerializeBinary(), |
| Yields(WhenParsed(EqualsTextProto(R"pb(optional_int32: 99)pb")))); |
| |
| EXPECT_CALL(runner_, RunTest("Recommended.Proto3.ProtobufInput." |
| "InfersTestNameFromCurrentTest.ProtobufOutput", |
| _)); |
| EXPECT_THAT( |
| Testee(kP1) |
| .ParseBinary(TestAllTypesProto3::descriptor(), VarintField(1, 99)) |
| .SerializeBinary(), |
| Yields(WhenParsed(EqualsTextProto(R"pb(optional_int32: 99)pb")))); |
| } |
| |
| // A ConformanceTest whose SetUp() and TearDown() leave the global environment |
| // alone, so that Testee() can be called without one. |
| class TesteeDeathTest : public ConformanceTest { |
| protected: |
| void SetUp() override {} |
| void TearDown() override {} |
| }; |
| |
| TEST_F(TesteeDeathTest, WithoutInstall) { EXPECT_DEATH(Testee(), "Install"); } |
| |
| TEST_F(TesteeDeathTest, AfterTearDown) { |
| FakeTestRunner runner; |
| internal::ScopedGlobalConformanceEnvironment env({/*runner=*/&runner}); |
| env->SetUp(); |
| env->TearDown(); |
| EXPECT_DEATH(Testee("Foo"), "already been shut down"); |
| } |
| |
| // --- Edition gating ---------------------------------------------------------- |
| |
| // SetUp() skips a test whose MessageUnderTest() is from an edition newer than |
| // --maximum_edition. EDITION_UNSTABLE counts as supported from EDITION_2023 |
| // on. |
| struct EditionGatingCase { |
| const Descriptor* message; |
| Edition maximum_edition; |
| bool supported; |
| }; |
| |
| class EditionGatingTest |
| : public FixtureTest, |
| public testing::WithParamInterface<EditionGatingCase> { |
| protected: |
| EditionGatingTest() |
| : FixtureTest(WithMaximumEdition(GetParam().maximum_edition)) {} |
| |
| void SetUp() override { |
| ConformanceTest::SetUp(); |
| EXPECT_EQ(IsSkipped(), !GetParam().supported); |
| } |
| |
| private: |
| static ConformanceEnvironmentOptions WithMaximumEdition(Edition edition) { |
| ConformanceEnvironmentOptions options; |
| options.maximum_edition = edition; |
| return options; |
| } |
| |
| const Descriptor* MessageUnderTest() const override { |
| return GetParam().message; |
| } |
| }; |
| |
| TEST_P(EditionGatingTest, SetUpSkipsUnsupportedMessages) { |
| EXPECT_TRUE(GetParam().supported) << "SetUp() should have skipped this test."; |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, EditionGatingTest, |
| testing::Values( |
| EditionGatingCase{TestAllTypesProto2::descriptor(), EDITION_PROTO3, |
| /*supported=*/true}, |
| EditionGatingCase{TestAllTypesProto3::descriptor(), EDITION_PROTO3, |
| /*supported=*/true}, |
| EditionGatingCase{TestAllTypesEdition2023::descriptor(), EDITION_PROTO3, |
| /*supported=*/false}, |
| EditionGatingCase{TestAllTypesEdition2023::descriptor(), EDITION_2023, |
| /*supported=*/true}, |
| EditionGatingCase{TestAllTypesEditionUnstable::descriptor(), |
| EDITION_PROTO3, /*supported=*/false}, |
| EditionGatingCase{TestAllTypesEditionUnstable::descriptor(), |
| EDITION_2023, /*supported=*/true}, |
| EditionGatingCase{TestAllTypesEditionUnstable::descriptor(), |
| EDITION_2024, /*supported=*/true}), |
| [](const testing::TestParamInfo<EditionGatingCase>& info) { |
| return absl::StrCat( |
| absl::StripPrefix(info.param.message->name(), "TestAllTypes"), "_", |
| Edition_Name(info.param.maximum_edition)); |
| }); |
| |
| // --- ConformanceTest fixture ------------------------------------------------- |
| |
| TEST_F(FixtureTest, RunsWithoutMessageUnderTest) { |
| EXPECT_THAT( |
| Testee() |
| .ParseBinary(TestAllTypesProto3::descriptor(), VarintField(1, 99)) |
| .SerializeBinary(), |
| Yields(WhenParsed(EqualsTextProto(R"pb(optional_int32: 99)pb")))); |
| } |
| |
| class Edition2023MessageTest : public FixtureTest { |
| protected: |
| using FixtureTest::FixtureTest; |
| |
| private: |
| const Descriptor* MessageUnderTest() const override { |
| return TestAllTypesEdition2023::descriptor(); |
| } |
| }; |
| |
| TEST_F(Edition2023MessageTest, IsSkippedInSetUpUnderDefaultMaximumEdition) { |
| FAIL() << "SetUp() should have skipped this test."; |
| } |
| |
| // TODO: Transitional. Goes away with the in-binary |
| // performance mode, see ConformanceEnvironmentOptions::performance. |
| class PerformanceFixtureTest : public FixtureTest { |
| protected: |
| bool IsPerformanceTest() const override { return true; } |
| }; |
| |
| TEST_F(PerformanceFixtureTest, IsSkippedWithoutPerformanceMode) { |
| FAIL() << "SetUp() should have skipped this test."; |
| } |
| |
| // Checks, from an overriding SetUp(), that the documented `if (IsSkipped()) |
| // return;` pattern sees the base class's skip. |
| class SkipAwareSetUpTest : public Edition2023MessageTest { |
| protected: |
| void SetUp() override { |
| ConformanceTest::SetUp(); |
| EXPECT_TRUE(IsSkipped()); |
| if (IsSkipped()) return; |
| FAIL() << "Unreachable."; |
| } |
| }; |
| |
| TEST_F(SkipAwareSetUpTest, SubclassSetUpSeesTheSkip) { |
| FAIL() << "SetUp() should have skipped this test."; |
| } |
| |
| // --- Priorities -------------------------------------------------------------- |
| |
| // ConformanceTest's DefaultPriority() is kP0, which is named "Required". |
| TEST_F(FixtureTest, TesteeDefaultsToTheSuitePriority) { |
| internal::TestResult result = |
| Testee() |
| .ParseBinary(TestAllTypesProto3::descriptor(), VarintField(1, 99)) |
| .SerializeBinary(); |
| EXPECT_EQ(result.priority(), kP0); |
| EXPECT_THAT(result.name(), StartsWith("Required.")); |
| EXPECT_THAT(result, |
| Yields(WhenParsed(EqualsTextProto(R"pb(optional_int32: 99)pb")))); |
| } |
| |
| TEST_F(FixtureTest, TesteeTakesAnExplicitPriority) { |
| internal::TestResult result = |
| Testee(kP1) |
| .ParseBinary(TestAllTypesProto3::descriptor(), VarintField(1, 99)) |
| .SerializeBinary(); |
| EXPECT_EQ(result.priority(), kP1); |
| EXPECT_THAT(result.name(), StartsWith("Recommended.")); |
| EXPECT_THAT(result, |
| Yields(WhenParsed(EqualsTextProto(R"pb(optional_int32: 99)pb")))); |
| } |
| |
| class P1FixtureTest : public FixtureTest { |
| private: |
| TestPriority DefaultPriority() const override { return kP1; } |
| }; |
| |
| TEST_F(P1FixtureTest, TesteeUsesTheOverriddenDefault) { |
| internal::TestResult result = |
| Testee() |
| .ParseBinary(TestAllTypesProto3::descriptor(), VarintField(1, 99)) |
| .SerializeBinary(); |
| EXPECT_EQ(result.priority(), kP1); |
| EXPECT_THAT(result.name(), StartsWith("Recommended.")); |
| EXPECT_THAT(result, |
| Yields(WhenParsed(EqualsTextProto(R"pb(optional_int32: 99)pb")))); |
| } |
| |
| TEST_F(P1FixtureTest, ExplicitPriorityBeatsTheDefault) { |
| internal::TestResult result = |
| Testee(kP0) |
| .ParseBinary(TestAllTypesProto3::descriptor(), VarintField(1, 99)) |
| .SerializeBinary(); |
| EXPECT_EQ(result.priority(), kP0); |
| EXPECT_THAT(result.name(), StartsWith("Required.")); |
| EXPECT_THAT(result, |
| Yields(WhenParsed(EqualsTextProto(R"pb(optional_int32: 99)pb")))); |
| } |
| |
| } // namespace |
| } // namespace conformance |
| } // namespace protobuf |
| } // namespace google |