Fix the test filter in the reported reproduction command. Use the same regression test name in the filter as during the GoogleTest test registration. To achieve this, I had to refactor the code a little bit. PiperOrigin-RevId: 895338387
diff --git a/fuzztest/internal/BUILD b/fuzztest/internal/BUILD index d6b0032..fe8a940 100644 --- a/fuzztest/internal/BUILD +++ b/fuzztest/internal/BUILD
@@ -224,14 +224,12 @@ ":configuration", ":corpus_database", ":flag_name", - ":io", ":registry", ":runtime", "@abseil-cpp//absl/status:statusor", "@abseil-cpp//absl/strings", "@abseil-cpp//absl/strings:str_format", "@abseil-cpp//absl/strings:string_view", - "@com_google_fuzztest//common:crashing_input_filename", "@com_google_fuzztest//common:logging", "@googletest//:gtest", ] + select({ @@ -405,6 +403,7 @@ "@abseil-cpp//absl/time", "@abseil-cpp//absl/types:span", "@com_google_fuzztest//common:bazel", + "@com_google_fuzztest//common:crashing_input_filename", "@com_google_fuzztest//common:logging", "@com_google_fuzztest//fuzztest/internal/domains:core_domains_impl", ], @@ -417,6 +416,7 @@ ":configuration", ":runtime", ":test_protobuf_cc_proto", + "@abseil-cpp//absl/status:statusor", "@abseil-cpp//absl/strings", "@abseil-cpp//absl/time", "@com_google_fuzztest//fuzztest:domain_core",
diff --git a/fuzztest/internal/CMakeLists.txt b/fuzztest/internal/CMakeLists.txt index 4ce2d47..d229aaf 100644 --- a/fuzztest/internal/CMakeLists.txt +++ b/fuzztest/internal/CMakeLists.txt
@@ -185,7 +185,6 @@ fuzztest::configuration fuzztest::corpus_database fuzztest::flag_name - fuzztest::io fuzztest::registry fuzztest::runtime GTest::gtest @@ -193,7 +192,6 @@ absl::strings absl::str_format absl::string_view - fuzztest::crashing_input_filename fuzztest::common_logging ) @@ -359,11 +357,11 @@ fuzztest::fixture_driver fuzztest::flag_name fuzztest::io - fuzztest::common_logging + fuzztest::logging fuzztest::printer fuzztest::registration - fuzztest::seed_seq fuzztest::sanitizer_interface + fuzztest::seed_seq fuzztest::serialization fuzztest::status absl::core_headers @@ -380,7 +378,8 @@ absl::time absl::span fuzztest::bazel - fuzztest::logging + fuzztest::common_logging + fuzztest::crashing_input_filename fuzztest::core_domains_impl ) @@ -393,10 +392,11 @@ fuzztest::configuration fuzztest::runtime fuzztest::test_protobuf_cc_proto + GTest::gmock_main + absl::statusor absl::strings absl::time fuzztest::domain_core - GTest::gmock_main ) fuzztest_cc_library(
diff --git a/fuzztest/internal/googletest_adaptor.cc b/fuzztest/internal/googletest_adaptor.cc index 70ffe85..d36d498 100644 --- a/fuzztest/internal/googletest_adaptor.cc +++ b/fuzztest/internal/googletest_adaptor.cc
@@ -3,7 +3,6 @@ #include <cstdlib> #include <string> -#include <string_view> #include <type_traits> #include <utility> #include <vector> @@ -13,12 +12,10 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" -#include "./common/crashing_input_filename.h" #include "./common/logging.h" #include "./fuzztest/internal/configuration.h" #include "./fuzztest/internal/corpus_database.h" #include "./fuzztest/internal/flag_name.h" -#include "./fuzztest/internal/io.h" #include "./fuzztest/internal/registry.h" #include "./fuzztest/internal/runtime.h" @@ -48,16 +45,29 @@ template <typename T> void RegisterFuzzTestAsGTest(int* argc, char*** argv, FuzzTest& test, const Configuration& configuration, - absl::string_view suffix = "") { + absl::string_view crashing_input_path = "") { auto fixture_factory = [argc, argv, &test, - configuration = configuration]() -> T* { - return new ::fuzztest::internal::GTest_TestAdaptor(test, argc, argv, - configuration); + configuration = configuration]() mutable -> T* { + return new ::fuzztest::internal::GTest_TestAdaptor( + test, argc, argv, std::move(configuration)); }; - const std::string test_name_with_suffix = - absl::StrCat(test.test_name(), suffix); + if (crashing_input_path.empty()) { + ::testing::RegisterTest(test.suite_name().c_str(), test.test_name().c_str(), + nullptr, nullptr, test.file().c_str(), test.line(), + std::move(fixture_factory)); + return; + } + const absl::StatusOr<std::string> regression_test_name = + RegressionTestNameForCrashingInput(test.test_name(), crashing_input_path); + if (!regression_test_name.ok()) { + FUZZTEST_LOG(WARNING) + << "Failed to get regression test name for crashing input " + << crashing_input_path << ". Not registering a regression test for it. " + << "Status: " << regression_test_name.status(); + return; + } ::testing::RegisterTest( - test.suite_name().c_str(), test_name_with_suffix.c_str(), nullptr, + test.suite_name().c_str(), regression_test_name->c_str(), nullptr, nullptr, test.file().c_str(), test.line(), std::move(fixture_factory)); } @@ -77,19 +87,7 @@ for (const std::string& input : crash_inputs) { Configuration updated_configuration = configuration; updated_configuration.crashing_input_to_reproduce = input; - absl::string_view file_name = Basename(input); - const absl::StatusOr<InputFileComponents> components = - ParseCrashingInputFilename( - std::string_view{file_name.data(), file_name.size()}); - if (!components.ok()) { - FUZZTEST_LOG(WARNING) - << "Failed to parse crashing input filename " << file_name - << ". Not registering a regression test for it. Status: " - << components.status(); - continue; - } - const std::string suffix = absl::StrCat("/Regression/", components->bug_id); - RegisterFuzzTestAsGTest<T>(argc, argv, test, updated_configuration, suffix); + RegisterFuzzTestAsGTest<T>(argc, argv, test, updated_configuration, input); } }
diff --git a/fuzztest/internal/runtime.cc b/fuzztest/internal/runtime.cc index 24e094f..cee5f50 100644 --- a/fuzztest/internal/runtime.cc +++ b/fuzztest/internal/runtime.cc
@@ -57,6 +57,7 @@ #include "absl/time/time.h" #include "absl/types/span.h" #include "./common/bazel.h" +#include "./common/crashing_input_filename.h" #include "./common/logging.h" #include "./fuzztest/internal/configuration.h" #include "./fuzztest/internal/corpus_database.h" @@ -92,11 +93,6 @@ constexpr absl::string_view kTrimIndicator = " ...<value too long>"; constexpr absl::string_view kReproducerDirName = "fuzztest_repro"; -std::string GetFilterForCrashingInput(absl::string_view test_name, - absl::string_view crashing_input_path) { - return absl::StrCat(test_name, "/Regression/", Basename(crashing_input_path)); -} - // Returns a reproduction command for replaying // `configuration.crashing_input_to_reproduce` or `reproducer_path` from a // command line, using the `configuration.reproduction_command_template`. @@ -130,11 +126,13 @@ } std::vector<std::string> extra_args = {absl::StrCat( "--test_arg=--", FUZZTEST_FLAG_PREFIX, "corpus_database=", corpus_db)}; + const absl::StatusOr<std::string> test_filter = + RegressionTestNameForCrashingInput( + test_name, *configuration->crashing_input_to_reproduce); + FUZZTEST_CHECK(test_filter.ok()); return absl::StrReplaceAll( command_template, - {{kTestFilterPlaceholder, - GetFilterForCrashingInput( - test_name, *configuration->crashing_input_to_reproduce)}, + {{kTestFilterPlaceholder, *test_filter}, {kExtraArgsPlaceholder, absl::StrJoin(extra_args, " ")}}); } else { return absl::StrReplaceAll( @@ -1352,4 +1350,12 @@ return success; } +absl::StatusOr<std::string> RegressionTestNameForCrashingInput( + absl::string_view test_name, absl::string_view crashing_input_path) { + absl::StatusOr<InputFileComponents> components = ParseCrashingInputFilename( + std::string_view{crashing_input_path.data(), crashing_input_path.size()}); + if (!components.ok()) return std::move(components).status(); + return absl::StrCat(test_name, "/Regression/", components->bug_id); +} + } // namespace fuzztest::internal
diff --git a/fuzztest/internal/runtime.h b/fuzztest/internal/runtime.h index ea867c3..1e0cbdd 100644 --- a/fuzztest/internal/runtime.h +++ b/fuzztest/internal/runtime.h
@@ -442,8 +442,13 @@ // A reproduction command template will include these placeholders. These // placeholders then will be replaced by the proper test filter when creating // the final reproduction command from the template. -static constexpr absl::string_view kTestFilterPlaceholder = "$TEST_FILTER"; -static constexpr absl::string_view kExtraArgsPlaceholder = "$EXTRA_ARGS"; +inline constexpr absl::string_view kTestFilterPlaceholder = "$TEST_FILTER"; +inline constexpr absl::string_view kExtraArgsPlaceholder = "$EXTRA_ARGS"; + +// Returns the name of a regression test that replays `test_name` on the +// crashing input at `crashing_input_path`. +absl::StatusOr<std::string> RegressionTestNameForCrashingInput( + absl::string_view test_name, absl::string_view crashing_input_path); } // namespace internal } // namespace fuzztest
diff --git a/fuzztest/internal/runtime_test.cc b/fuzztest/internal/runtime_test.cc index 624a990..16dc868 100644 --- a/fuzztest/internal/runtime_test.cc +++ b/fuzztest/internal/runtime_test.cc
@@ -19,6 +19,7 @@ #include <utility> #include "gtest/gtest.h" +#include "absl/status/statusor.h" #include "absl/strings/match.h" #include "absl/time/time.h" #include "./fuzztest/domain_core.h" @@ -94,5 +95,22 @@ EXPECT_EQ(get_failure(), ""); } +TEST(RegressionTestNameForCrashingInputTest, + ReturnsRegressionTestNameForValidInputFilename) { + const absl::StatusOr<std::string> regression_test_name = + RegressionTestNameForCrashingInput("MySuite.MyTest", + "/path/to/bug_id-crash_sig-input_sig"); + ASSERT_TRUE(regression_test_name.ok()); + EXPECT_EQ(*regression_test_name, "MySuite.MyTest/Regression/bug_id"); +} + +TEST(RegressionTestNameForCrashingInputTest, + ReturnsErrorForInvalidInputFilename) { + const absl::StatusOr<std::string> regression_test_name = + RegressionTestNameForCrashingInput("MySuite.MyTest", + "single_dash-is_invalid"); + EXPECT_FALSE(regression_test_name.ok()); +} + } // namespace } // namespace fuzztest::internal