Fail loudly when FUZZ_TEST is used with custom main() without InitFuzzTest PiperOrigin-RevId: 999201288
diff --git a/cmake/FuzzTestHelpers.cmake b/cmake/FuzzTestHelpers.cmake index 354e241..2869a77 100644 --- a/cmake/FuzzTestHelpers.cmake +++ b/cmake/FuzzTestHelpers.cmake
@@ -433,7 +433,7 @@ set(generated_include_dir "${generated_target_dir}") if (NOT ${FLATBUFFERS_GENERATE_HEADERS_INCLUDE_PREFIX} STREQUAL "") set(generated_include_dir "${generated_include_dir}/${FLATBUFFERS_GENERATE_HEADERS_INCLUDE_PREFIX}") - list(APPEND FLATBUFFERS_GENERATE_HEADERS_FLAGS + list(APPEND FLATBUFFERS_GENERATE_HEADERS_FLAGS "--include-prefix" ${FLATBUFFERS_GENERATE_HEADERS_INCLUDE_PREFIX}) endif()
diff --git a/e2e_tests/BUILD b/e2e_tests/BUILD index b392ee6..e58abc9 100644 --- a/e2e_tests/BUILD +++ b/e2e_tests/BUILD
@@ -48,7 +48,6 @@ "@com_google_fuzztest//centipede:centipede_uninstrumented", "@com_google_fuzztest//e2e_tests/testdata:data", "@com_google_fuzztest//e2e_tests/testdata:dynamically_registered_fuzz_tests.stripped", - "@com_google_fuzztest//e2e_tests/testdata:fuzz_test_without_init_fuzztest.stripped", "@com_google_fuzztest//e2e_tests/testdata:fuzz_tests_for_functional_testing.stripped", "@com_google_fuzztest//e2e_tests/testdata:fuzz_tests_with_invalid_seeds.stripped", "@com_google_fuzztest//e2e_tests/testdata:llvm_fuzzer_with_custom_mutator.stripped",
diff --git a/e2e_tests/functional_test.cc b/e2e_tests/functional_test.cc index c3263b2..943eb72 100644 --- a/e2e_tests/functional_test.cc +++ b/e2e_tests/functional_test.cc
@@ -730,16 +730,6 @@ Optional(IsXmlWithExactlyFuzzTestsHavingFuzzTestProperty())); } -TEST_F(UnitTestModeTest, FailsLoudlyWhenInitFuzzTestIsNotCalled) { - auto [status, std_out, std_err] = - RunWithExactFuzzerFlags("*", "testdata/fuzz_test_without_init_fuzztest"); - - EXPECT_THAT(status, Ne(ExitCode(0))); - EXPECT_THAT_LOG( - std_out, HasSubstr("FUZZ_TEST(MySuite, MyFuzzTest) was registered, but " - "InitFuzzTest was never called in main().")); -} - // Tests for the FuzzTest command line interface. class GenericCommandLineInterfaceTest : public ::testing::Test { protected:
diff --git a/e2e_tests/testdata/BUILD b/e2e_tests/testdata/BUILD index bc41e92..0db3a80 100644 --- a/e2e_tests/testdata/BUILD +++ b/e2e_tests/testdata/BUILD
@@ -138,16 +138,6 @@ ) cc_binary( - name = "fuzz_test_without_init_fuzztest", - testonly = 1, - srcs = ["fuzz_test_without_init_fuzztest.cc"], - deps = [ - "@com_google_fuzztest//fuzztest", - "@googletest//:gtest", - ], -) - -cc_binary( name = "fuzz_tests_for_corpus_database_testing", testonly = 1, srcs = ["fuzz_tests_for_corpus_database_testing.cc"],
diff --git a/e2e_tests/testdata/CMakeLists.txt b/e2e_tests/testdata/CMakeLists.txt index c1b7eb5..ec93a80 100644 --- a/e2e_tests/testdata/CMakeLists.txt +++ b/e2e_tests/testdata/CMakeLists.txt
@@ -116,19 +116,3 @@ PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/_main/e2e_tests/testdata" ) - -add_executable( - fuzz_test_without_init_fuzztest.stripped - fuzz_test_without_init_fuzztest.cc -) -target_link_libraries( - fuzz_test_without_init_fuzztest.stripped - PRIVATE - fuzztest::fuzztest - GTest::gtest -) -set_target_properties( - fuzz_test_without_init_fuzztest.stripped - PROPERTIES RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/_main/e2e_tests/testdata" -)
diff --git a/e2e_tests/testdata/fuzz_test_without_init_fuzztest.cc b/e2e_tests/testdata/fuzz_test_without_init_fuzztest.cc deleted file mode 100644 index 8f95aed..0000000 --- a/e2e_tests/testdata/fuzz_test_without_init_fuzztest.cc +++ /dev/null
@@ -1,28 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "gtest/gtest.h" -#include "./fuzztest/fuzztest.h" - -namespace { - -void MyFuzzTest(int) {} -FUZZ_TEST(MySuite, MyFuzzTest); - -} // namespace - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}
diff --git a/fuzztest/BUILD b/fuzztest/BUILD index f6fb89e..ab25066 100644 --- a/fuzztest/BUILD +++ b/fuzztest/BUILD
@@ -55,13 +55,10 @@ cc_library( name = "fuzztest", - testonly = True, hdrs = ["fuzztest.h"], deps = [ ":domain", ":fuzztest_macros", - # TODO(b/570019271): Remove dependency once bug is fixed. - "@com_google_fuzztest//fuzztest/internal:googletest_adaptor", ], ) @@ -164,6 +161,7 @@ testonly = True, srcs = ["llvm_fuzzer_wrapper.cc"], deps = [ + ":fuzztest", ":fuzztest_macros", "@abseil-cpp//absl/base:core_headers", "@abseil-cpp//absl/base:no_destructor",
diff --git a/fuzztest/CMakeLists.txt b/fuzztest/CMakeLists.txt index a06867a..c42d529 100644 --- a/fuzztest/CMakeLists.txt +++ b/fuzztest/CMakeLists.txt
@@ -92,7 +92,6 @@ DEPS fuzztest::domain fuzztest::fuzztest_macros - fuzztest::googletest_adaptor ) fuzztest_cc_library(
diff --git a/fuzztest/init_fuzztest.cc b/fuzztest/init_fuzztest.cc index 5b09d54..74c1bc5 100644 --- a/fuzztest/init_fuzztest.cc +++ b/fuzztest/init_fuzztest.cc
@@ -447,8 +447,6 @@ void InitFuzzTest(int* argc, char*** argv, std::string_view binary_id) { auto& runtime = internal::Runtime::instance(); - runtime.SetInitFuzzTestCalled(); - runtime.SetArgs(argc, argv); const bool is_listing = absl::GetFlag(FUZZTEST_FLAG(list_fuzz_tests)); if (is_listing) { for (const auto& name : ListRegisteredTests()) { @@ -496,9 +494,7 @@ internal::Configuration configuration = CreateConfigurationsFromFlags(derived_binary_id); configuration.reproduction_command_template = reproduction_command_template; - runtime.SetInitConfiguration(configuration); - internal::RegisterSeparateRegressionTestsForEachCrashingInput(configuration); - internal::RegisterGoogleTestEventListener(); + internal::RegisterFuzzTestsAsGoogleTests(argc, argv, configuration); const bool is_fuzzing_or_replaying = (fuzzing_time_limit || replay_corpus_time_limit);
diff --git a/fuzztest/internal/BUILD b/fuzztest/internal/BUILD index 27f9e67..1219ada 100644 --- a/fuzztest/internal/BUILD +++ b/fuzztest/internal/BUILD
@@ -383,7 +383,6 @@ "@abseil-cpp//absl/container:flat_hash_map", "@abseil-cpp//absl/functional:function_ref", "@abseil-cpp//absl/strings:string_view", - "@com_google_fuzztest//common:logging", ] + select({ "@com_google_fuzztest//fuzztest:use_centipede": [":centipede_adaptor"], "//conditions:default": [],
diff --git a/fuzztest/internal/CMakeLists.txt b/fuzztest/internal/CMakeLists.txt index 4f90993..1ccc8a3 100644 --- a/fuzztest/internal/CMakeLists.txt +++ b/fuzztest/internal/CMakeLists.txt
@@ -352,7 +352,6 @@ SRCS "registry.cc" DEPS - fuzztest::common_logging fuzztest::compatibility_mode fuzztest::fixture_driver fuzztest::registration
diff --git a/fuzztest/internal/googletest_adaptor.cc b/fuzztest/internal/googletest_adaptor.cc index 44f9f90..d36d498 100644 --- a/fuzztest/internal/googletest_adaptor.cc +++ b/fuzztest/internal/googletest_adaptor.cc
@@ -2,7 +2,6 @@ #include "./fuzztest/internal/googletest_adaptor.h" #include <cstdlib> -#include <optional> #include <string> #include <type_traits> #include <utility> @@ -26,13 +25,12 @@ namespace fuzztest::internal { -std::vector<std::string> GTest_TestAdaptor::GetFuzzTestsInCurrentShard( - const Configuration& configuration) const { +std::vector<std::string> GTest_TestAdaptor::GetFuzzTestsInCurrentShard() const { std::vector<std::string> result; for (const auto* test : GetRegisteredTests()) { if (!test->should_run()) continue; if (test->is_in_another_shard()) continue; - for (const auto& fuzztest : configuration.fuzz_tests) { + for (const auto& fuzztest : configuration_.fuzz_tests) { if (fuzztest == absl::StrCat(test->test_suite_name(), ".", test->name())) { result.push_back(fuzztest); @@ -45,12 +43,13 @@ namespace { template <typename T> -void RegisterFuzzTestAsGTest( - FuzzTest& test, std::optional<Configuration> configuration = std::nullopt, - absl::string_view crashing_input_path = "") { - auto fixture_factory = [&test, - configuration = std::move(configuration)]() -> T* { - return new ::fuzztest::internal::GTest_TestAdaptor(test, configuration); +void RegisterFuzzTestAsGTest(int* argc, char*** argv, FuzzTest& test, + const Configuration& configuration, + absl::string_view crashing_input_path = "") { + auto fixture_factory = [argc, argv, &test, + configuration = configuration]() mutable -> T* { + return new ::fuzztest::internal::GTest_TestAdaptor( + test, argc, argv, std::move(configuration)); }; if (crashing_input_path.empty()) { ::testing::RegisterTest(test.suite_name().c_str(), test.test_name().c_str(), @@ -73,8 +72,9 @@ } template <typename T> -void RegisterRegressionTestsForTest(FuzzTest& test, - const Configuration& configuration) { +void RegisterSeparateRegressionTestForEachCrashingInput( + int* argc, char*** argv, FuzzTest& test, + const Configuration& configuration) { if (!configuration.reproduce_findings_as_separate_tests) return; #ifdef FUZZTEST_USE_CENTIPEDE const std::vector<std::string> crash_inputs = @@ -87,33 +87,31 @@ for (const std::string& input : crash_inputs) { Configuration updated_configuration = configuration; updated_configuration.crashing_input_to_reproduce = input; - RegisterFuzzTestAsGTest<T>(test, updated_configuration, input); + RegisterFuzzTestAsGTest<T>(argc, argv, test, updated_configuration, input); } } +template <typename T> +void RegisterTests(int* argc, char*** argv, FuzzTest& test, + const Configuration& configuration) { + RegisterFuzzTestAsGTest<T>(argc, argv, test, configuration); + RegisterSeparateRegressionTestForEachCrashingInput<T>(argc, argv, test, + configuration); +} + } // namespace -void RegisterFuzzTestAsGoogleTest(FuzzTest& test) { - if (test.uses_fixture()) { - RegisterFuzzTestAsGTest<::fuzztest::internal::GTest_TestAdaptor>(test); - } else { - RegisterFuzzTestAsGTest<::testing::Test>(test); - } -} - -void RegisterSeparateRegressionTestsForEachCrashingInput( - const Configuration& configuration) { +void RegisterFuzzTestsAsGoogleTests(int* argc, char*** argv, + const Configuration& configuration) { ::fuzztest::internal::ForEachTest([&](auto& test) { if (test.uses_fixture()) { - RegisterRegressionTestsForTest<::fuzztest::internal::GTest_TestAdaptor>( - test, configuration); + RegisterTests<::fuzztest::internal::GTest_TestAdaptor>(argc, argv, test, + configuration); } else { - RegisterRegressionTestsForTest<::testing::Test>(test, configuration); + RegisterTests<::testing::Test>(argc, argv, test, configuration); } }); -} -void RegisterGoogleTestEventListener() { ::testing::UnitTest::GetInstance()->listeners().Append( new ::fuzztest::internal::GTest_EventListener< ::testing::EmptyTestEventListener, ::testing::TestPartResult>());
diff --git a/fuzztest/internal/googletest_adaptor.h b/fuzztest/internal/googletest_adaptor.h index 0303d75..39ffcab 100644 --- a/fuzztest/internal/googletest_adaptor.h +++ b/fuzztest/internal/googletest_adaptor.h
@@ -16,7 +16,6 @@ #define FUZZTEST_FUZZTEST_GOOGLETEST_ADAPTOR_H_ #include <cstdlib> -#include <optional> #include <string> #include <utility> #include <vector> @@ -31,33 +30,23 @@ class GTest_TestAdaptor : public ::testing::Test { public: - explicit GTest_TestAdaptor( - FuzzTest& test, std::optional<Configuration> configuration = std::nullopt) - : test_(test), configuration_(std::move(configuration)) {} + explicit GTest_TestAdaptor(FuzzTest& test, int* argc, char*** argv, + Configuration configuration) + : test_(test), + argc_(argc), + argv_(argv), + configuration_(std::move(configuration)) {} void TestBody() override { - if (!Runtime::instance().init_fuzztest_called()) { - ADD_FAILURE() - << "FUZZ_TEST(" << test_.suite_name() << ", " << test_.test_name() - << ") was registered, but InitFuzzTest was never called in main(). " - << "If you are using a custom main(), please call " - << "fuzztest::InitFuzzTest(&argc, &argv)" - << " before RUN_ALL_TESTS()."; - return; - } RecordProperty("fuzz_test", "true"); auto test = test_.make(); - Configuration configuration = - configuration_.has_value() ? *configuration_ - : Runtime::instance().init_configuration(); - configuration.fuzz_tests_in_current_shard = - GetFuzzTestsInCurrentShard(configuration); + configuration_.fuzz_tests_in_current_shard = GetFuzzTestsInCurrentShard(); // We replay a reproducer in the same process to help debugging when // (1) we're replaying a single reproducer and (2) we're running locally. const bool running_locally = !std::getenv("FUZZTEST_RUNNING_UNDER_CI"); - configuration.replay_in_single_process = - configuration.crashing_input_to_reproduce.has_value() && + configuration_.replay_in_single_process = + configuration_.crashing_input_to_reproduce.has_value() && testing::UnitTest::GetInstance()->test_to_run_count() == 1 && running_locally; if (Runtime::instance().run_mode() == RunMode::kUnitTest) { @@ -65,8 +54,8 @@ // bugs, i.e., run multiple tests that lead to a crash. #if defined(GTEST_HAS_DEATH_TEST) && !defined(FUZZTEST_USE_CENTIPEDE) const bool needs_subprocess = - configuration.crashing_input_to_reproduce.has_value() && - (!configuration.replay_in_single_process || + configuration_.crashing_input_to_reproduce.has_value() && + (!configuration_.replay_in_single_process || // EXPECT_EXIT is required in the death-test subprocess, but in // the subprocess there's only one test to run. testing::internal::InDeathTestChild()); @@ -74,7 +63,7 @@ const bool needs_subprocess = false; #endif if (needs_subprocess) { - configuration.preprocess_crash_reproducing = [] { + configuration_.preprocess_crash_reproducing = [] { // EXPECT_EXIT disables event forwarding in gtest and as a result, // EXPECT/ASSERT-s are disabled. Here, we overwrite this option. testing::UnitTest::GetInstance()->listeners().SuppressEventForwarding( @@ -87,7 +76,7 @@ // test below fails without terminating the process. #ifdef GTEST_HAS_DEATH_TEST EXPECT_EXIT( - (test->RunInUnitTestMode(configuration), + (test->RunInUnitTestMode(configuration_), void( R"( FuzzTest failure! Please see 'actual message' below for the crash report. )"), std::exit(0)), @@ -96,7 +85,7 @@ EXPECT_TRUE(false) << "Death test is not supported."; #endif } else { - EXPECT_TRUE(test->RunInUnitTestMode(configuration) || + EXPECT_TRUE(test->RunInUnitTestMode(configuration_) || Runtime::instance().skipping_requested()) << "Failure(s) found in the unit-test mode - please see the test " "log for more details."; @@ -104,9 +93,7 @@ } else { // TODO(b/245753736): Consider using `tolerate_failure` when FuzzTest can // tolerate crashes in fuzzing mode. - EXPECT_TRUE(test->RunInFuzzingMode(Runtime::instance().argc(), - Runtime::instance().argv(), - configuration) || + EXPECT_TRUE(test->RunInFuzzingMode(argc_, argv_, configuration_) || Runtime::instance().skipping_requested()) << "Failure(s) found in the fuzzing mode - please see the test log " "for more details."; @@ -126,11 +113,12 @@ } private: - std::vector<std::string> GetFuzzTestsInCurrentShard( - const Configuration& configuration) const; + std::vector<std::string> GetFuzzTestsInCurrentShard() const; FuzzTest& test_; - std::optional<Configuration> configuration_; + int* argc_; + char*** argv_; + Configuration configuration_; }; template <typename Base, typename TestPartResult> @@ -154,14 +142,9 @@ } }; -// Registers a separate GoogleTest TEST for each crashing input of each -// FUZZ_TEST when `configuration.reproduce_findings_as_separate_tests` is true. -void RegisterSeparateRegressionTestsForEachCrashingInput( - const Configuration& configuration); - -// Registers a GoogleTest event listener that reports assertion failures to -// FuzzTest. -void RegisterGoogleTestEventListener(); +// Registers FUZZ_TEST as GoogleTest TEST-s. +void RegisterFuzzTestsAsGoogleTests(int* argc, char*** argv, + const Configuration& configuration); // Set listing mode validator for GoogleTest to check that fuzz test listing was // properly handled.
diff --git a/fuzztest/internal/registry.cc b/fuzztest/internal/registry.cc index 563f684..ac695f0 100644 --- a/fuzztest/internal/registry.cc +++ b/fuzztest/internal/registry.cc
@@ -21,7 +21,6 @@ #include "absl/container/flat_hash_map.h" #include "absl/functional/function_ref.h" #include "absl/strings/string_view.h" -#include "./common/logging.h" #include "./fuzztest/internal/registration.h" #include "./fuzztest/internal/runtime.h" @@ -58,10 +57,8 @@ for (auto& t : Regs()) func(t); } -FuzzTest& RegisterImpl(BasicTestInfo test_info, FuzzTestFuzzerFactory factory) { - FUZZTEST_CHECK(!Runtime::instance().init_fuzztest_called()) - << "RegisterFuzzTest() must be called before InitFuzzTest()."; - return Regs().emplace_back(std::move(test_info), std::move(factory)); +void RegisterImpl(BasicTestInfo test_info, FuzzTestFuzzerFactory factory) { + Regs().emplace_back(std::move(test_info), std::move(factory)); } void RegisterSetUpTearDownTestSuiteFunctions(
diff --git a/fuzztest/internal/registry.h b/fuzztest/internal/registry.h index 77acabc..0e8a446 100644 --- a/fuzztest/internal/registry.h +++ b/fuzztest/internal/registry.h
@@ -34,10 +34,7 @@ namespace fuzztest { namespace internal { -FuzzTest& RegisterImpl(BasicTestInfo test_info, FuzzTestFuzzerFactory factory); - -// TODO(b/570019271): Remove declaration once bug is fixed. -void RegisterFuzzTestAsGoogleTest(FuzzTest& test); +void RegisterImpl(BasicTestInfo test_info, FuzzTestFuzzerFactory factory); void ForEachTest(absl::FunctionRef<void(FuzzTest&)> func); @@ -64,8 +61,8 @@ &Fixture::TearDownTestSuite); } BasicTestInfo test_info = reg.test_info_; - RegisterFuzzTestAsGoogleTest(RegisterImpl( - std::move(test_info), GetFuzzTestFuzzerFactory(std::move(reg)))); + RegisterImpl(std::move(test_info), + GetFuzzTestFuzzerFactory(std::move(reg))); return *this; } @@ -125,7 +122,8 @@ // fuzz test registration by calling functions `GetRegistration()` and // `GetRegistrationWithFixture()`. // -// Make sure to only call this function before calling `InitFuzzTest()`. +// Make sure to only call this function before calling +// `RegisterFuzzTestsAsGoogleTests()`. template <typename Registration> void RegisterFuzzTest(Registration&& reg) { ::fuzztest::internal::RegistrationToken{} = std::forward<Registration>(reg);
diff --git a/fuzztest/internal/runtime.h b/fuzztest/internal/runtime.h index 38d16a9..1e0cbdd 100644 --- a/fuzztest/internal/runtime.h +++ b/fuzztest/internal/runtime.h
@@ -163,28 +163,6 @@ void SetRunMode(RunMode run_mode) { run_mode_ = run_mode; } RunMode run_mode() const { return run_mode_; } - // Records that `InitFuzzTest` has been called. - void SetInitFuzzTestCalled() { init_fuzztest_called_ = true; } - bool init_fuzztest_called() const { return init_fuzztest_called_; } - - // Sets the command-line arguments passed to `InitFuzzTest`. `argc()` and - // `argv()` are only valid after `InitFuzzTest` has been called. - void SetArgs(int* argc, char*** argv) { - argc_ = argc; - argv_ = argv; - } - int* argc() const { return argc_; } - char*** argv() const { return argv_; } - - // Sets the initial configuration created by `InitFuzzTest`. - // `init_configuration()` is only valid after `InitFuzzTest` has been called. - void SetInitConfiguration(Configuration configuration) { - init_configuration_ = std::move(configuration); - } - const Configuration& init_configuration() const { - return init_configuration_; - } - // Enables the crash reporter. // REQUIRES: `SetCurrentTest()` has been called with non-null arguments. void EnableReporter(const RuntimeStats* stats, absl::Time (*clock_fn)()) { @@ -287,11 +265,6 @@ const RuntimeStats* stats_ = nullptr; absl::Time (*clock_fn_)() = nullptr; - bool init_fuzztest_called_ = false; - int* argc_ = nullptr; - char*** argv_ = nullptr; - Configuration init_configuration_; - // We use a simple custom spinlock instead of absl::Mutex to reduce // dependencies and avoid potential issues with code instrumentation. class ABSL_LOCKABLE Spinlock {
diff --git a/fuzztest/llvm_fuzzer_wrapper.cc b/fuzztest/llvm_fuzzer_wrapper.cc index ccaead0..95e3d7c 100644 --- a/fuzztest/llvm_fuzzer_wrapper.cc +++ b/fuzztest/llvm_fuzzer_wrapper.cc
@@ -16,6 +16,7 @@ #include "absl/random/random.h" #include "absl/synchronization/mutex.h" #include "./common/logging.h" +#include "./fuzztest/fuzztest.h" #include "./fuzztest/fuzztest_macros.h" #include "./fuzztest/internal/domains/arbitrary_impl.h" #include "./fuzztest/internal/domains/container_of_impl.h"