blob: 5958da958803289c8a20d9a61063e8756b3845be [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
#include "conformance/test_fixture.h"
#include <string>
#include "google/protobuf/descriptor.pb.h"
#include <gtest/gtest.h>
#include "absl/container/flat_hash_map.h"
#include "absl/log/absl_check.h"
#include "absl/strings/string_view.h"
#include "conformance/test_environment.h"
#include "conformance/testee.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor_legacy.h"
namespace google {
namespace protobuf {
namespace conformance {
namespace {
using ::google::protobuf::conformance::internal::ConformanceEnvironment;
using ::google::protobuf::conformance::internal::Statistics;
// Whether tests for `descriptor`'s message type run under `maximum_edition`:
// the file's edition must not be newer than it.
bool IsSupported(const Descriptor& descriptor, Edition maximum_edition) {
Edition edition = FileDescriptorLegacy(descriptor.file()).edition();
if (edition == EDITION_UNSTABLE) {
// `maximum_edition` is the maximum *stable* edition. Like the legacy
// runner, run the unstable tests alongside any editions tests.
// TODO: this runs the (newer-than-anything) unstable tests
// at --maximum_edition=2023 while a stable 2024 message would be skipped;
// gate EDITION_UNSTABLE on its own flag once legacy parity no longer
// matters.
return maximum_edition >= EDITION_2023;
}
return edition <= maximum_edition;
}
// Statistics accumulated by ConformanceTest::TearDown() for each test suite,
// keyed by suite name, until TearDownTestSuite() reports them.
absl::flat_hash_map<std::string, Statistics>& SuiteStatistics() {
static auto* suite_statistics =
new absl::flat_hash_map<std::string, Statistics>();
return *suite_statistics;
}
std::string CurrentTestName() {
const testing::TestInfo* test_info =
testing::UnitTest::GetInstance()->current_test_info();
ABSL_CHECK(test_info != nullptr)
<< "Testee() can only infer the test name from inside a test body; "
"pass a name explicitly otherwise.";
return test_info->name();
}
std::string CurrentTestSuiteName() {
const testing::TestSuite* test_suite =
testing::UnitTest::GetInstance()->current_test_suite();
ABSL_CHECK(test_suite != nullptr)
<< "ConformanceTest's TearDown hooks must run inside a test suite.";
return test_suite->name();
}
} // namespace
void ConformanceTest::SetUp() {
ConformanceEnvironment& environment = ConformanceEnvironment::Get();
initial_statistics_ = Statistics::From(environment.test_manager());
// TODO: Transitional. Goes away with the in-binary
// performance mode, see ConformanceEnvironmentOptions::performance.
if (IsPerformanceTest() && !environment.options().performance) {
GTEST_SKIP() << "Performance tests only run with --performance.";
}
if (!IsPerformanceTest() && environment.options().performance) {
GTEST_SKIP() << "Only performance tests run with --performance.";
}
if (const Descriptor* message = MessageUnderTest();
message != nullptr &&
!IsSupported(*message, environment.options().maximum_edition)) {
GTEST_SKIP() << "Skipping " << message->full_name()
<< " because its edition is newer than --maximum_edition.";
}
}
void ConformanceTest::TearDown() {
Statistics delta =
Statistics::From(ConformanceEnvironment::Get().test_manager()) -
initial_statistics_;
delta.RecordProperties();
SuiteStatistics()[CurrentTestSuiteName()] += delta;
}
void ConformanceTest::TearDownTestSuite() {
auto node = SuiteStatistics().extract(CurrentTestSuiteName());
if (!node) {
// gtest only runs TearDownTestSuite() for suites with at least one test to
// run, and TearDown() runs even for tests skipped in SetUp(), so this only
// happens when a fatal failure in a SetUpTestSuite() skipped every test
// outright (which is already reported), or when a subclass's TearDown()
// doesn't call ConformanceTest::TearDown().
return;
}
node.mapped().RecordProperties();
}
internal::Test ConformanceTest::Testee() { return Testee(CurrentTestName()); }
internal::Test ConformanceTest::Testee(absl::string_view name) {
return Testee(DefaultPriority(), name);
}
internal::Test ConformanceTest::Testee(TestPriority priority) {
return Testee(priority, CurrentTestName());
}
internal::Test ConformanceTest::Testee(TestPriority priority,
absl::string_view name) {
return ConformanceEnvironment::Get().testee().CreateTest(name, priority);
}
} // namespace conformance
} // namespace protobuf
} // namespace google