From f0d510faa9893d2e4181df1ed696f842fa4d0afd Mon Sep 17 00:00:00 2001 From: Xinhao Yuan Date: Fri, 6 Dec 2024 11:20:10 -0800 Subject: [PATCH] Rewrite a few tests to prepare for the unified execution model. PiperOrigin-RevId: 703558952 --- centipede/BUILD | 5 ++ centipede/centipede_default_callbacks.cc | 6 ++ centipede/runner.cc | 8 +- centipede/runner_interface.h | 3 + centipede/seed_corpus_maker_lib.cc | 85 ++++++++++++++++--- centipede/seed_corpus_maker_lib.h | 7 +- centipede/seed_corpus_maker_lib_test.cc | 69 +++++++++++++++ common/remote_file_oss.cc | 4 + e2e_tests/functional_test.cc | 83 +++++++++--------- .../fuzz_tests_for_functional_testing.cc | 17 +++- .../testdata/fuzz_tests_using_googletest.cc | 13 ++- fuzztest/internal/centipede_adaptor.cc | 2 +- 12 files changed, 232 insertions(+), 70 deletions(-) diff --git a/centipede/BUILD b/centipede/BUILD index 6835c2ed..39282425 100644 --- a/centipede/BUILD +++ b/centipede/BUILD @@ -913,6 +913,7 @@ cc_library( ":environment", ":mutation_input", ":runner_result", + ":stop", "@com_google_absl//absl/log", "@com_google_absl//absl/log:check", "@com_google_fuzztest//common:defs", @@ -1121,6 +1122,7 @@ cc_library( ":thread_pool", ":util", ":workdir", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/log", "@com_google_absl//absl/log:check", "@com_google_absl//absl/random", @@ -1688,8 +1690,11 @@ cc_test( ":feature", ":seed_corpus_maker_lib", ":workdir", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/strings", + "@com_google_fuzztest//common:defs", "@com_google_fuzztest//common:logging", + "@com_google_fuzztest//common:remote_file", "@com_google_fuzztest//common:test_util", "@com_google_googletest//:gtest_main", ], diff --git a/centipede/centipede_default_callbacks.cc b/centipede/centipede_default_callbacks.cc index 6d211215..80203d55 100644 --- a/centipede/centipede_default_callbacks.cc +++ b/centipede/centipede_default_callbacks.cc @@ -25,6 +25,7 @@ #include "./centipede/environment.h" #include "./centipede/mutation_input.h" #include "./centipede/runner_result.h" +#include "./centipede/stop.h" #include "./common/defs.h" #include "./common/logging.h" // IWYU pragma: keep @@ -83,6 +84,11 @@ void CentipedeDefaultCallbacks::Mutate( LOG_FIRST_N(WARNING, 5) << "Custom mutator returned no mutants: falling back to internal " "default mutator"; + } else if (ShouldStop()) { + LOG(WARNING) << "Custom mutator failed, but ignored since the stop " + "condition it met. Possibly what triggered the stop " + "condition also interrupted the mutator."; + return; } else { LOG(WARNING) << "Custom mutator undetected or misbehaving:"; CHECK(!custom_mutator_is_usable_.has_value()) diff --git a/centipede/runner.cc b/centipede/runner.cc index 31950352..e5714a03 100644 --- a/centipede/runner.cc +++ b/centipede/runner.cc @@ -614,7 +614,9 @@ static void RunOneInput(const uint8_t *data, size_t size, int target_return_value = callbacks.Execute({data, size}) ? 0 : -1; state.stats.exec_time_usec = UsecSinceLast(); CheckWatchdogLimits(); - PostProcessCoverage(target_return_value); + if (centipede::state.input_start_time.exchange(0) != 0) { + PostProcessCoverage(target_return_value); + } state.stats.post_time_usec = UsecSinceLast(); state.stats.peak_rss_mb = GetPeakRSSMb(); } @@ -1235,7 +1237,9 @@ extern "C" void CentipedePrepareProcessing() { extern "C" void CentipedeFinalizeProcessing() { centipede::CheckWatchdogLimits(); - centipede::PostProcessCoverage(/*target_return_value=*/0); + if (centipede::state.input_start_time.exchange(0) != 0) { + centipede::PostProcessCoverage(/*target_return_value=*/0); + } } extern "C" size_t CentipedeGetExecutionResult(uint8_t *data, size_t capacity) { diff --git a/centipede/runner_interface.h b/centipede/runner_interface.h index 3227cc47..7fd75811 100644 --- a/centipede/runner_interface.h +++ b/centipede/runner_interface.h @@ -103,6 +103,9 @@ extern "C" void CentipedeEndExecutionBatch(); extern "C" void CentipedePrepareProcessing(); // Finalizes the processing of an input and stores the state internally. +// +// For tool integration, it can be called inside `RunnerCallbacks::Execute()` to +// finalize the execution early before extra cleanups. extern "C" void CentipedeFinalizeProcessing(); // Retrieves the execution results (including coverage information) after diff --git a/centipede/seed_corpus_maker_lib.cc b/centipede/seed_corpus_maker_lib.cc index 677ebc4c..509d892d 100644 --- a/centipede/seed_corpus_maker_lib.cc +++ b/centipede/seed_corpus_maker_lib.cc @@ -36,6 +36,7 @@ #include #include +#include "absl/container/flat_hash_set.h" #include "absl/log/check.h" #include "absl/log/log.h" #include "absl/random/random.h" @@ -115,21 +116,54 @@ absl::Status SampleSeedCorpusElementsFromSource( // LOG(INFO) << "Selected " << src_dirs.size() << " corpus dir(s)"; } - // Find all the corpus shard files in the found dirs. + // Find all the corpus shard and individual input files in the found dirs. std::vector corpus_shard_fnames; + std::vector individual_input_fnames; for (const auto& dir : src_dirs) { - const std::string shards_glob = fs::path{dir} / source.shard_rel_glob; - // NOTE: `RemoteGlobMatch` appends to the output list. - const auto prev_num_shards = corpus_shard_fnames.size(); - RETURN_IF_NOT_OK(RemoteGlobMatch(shards_glob, corpus_shard_fnames)); - LOG(INFO) << "Found " << (corpus_shard_fnames.size() - prev_num_shards) - << " shard(s) matching " << shards_glob; + absl::flat_hash_set current_corpus_shard_fnames; + if (!source.shard_rel_glob.empty()) { + std::vector matched_fnames; + const std::string glob = fs::path{dir} / source.shard_rel_glob; + const auto match_status = RemoteGlobMatch(glob, matched_fnames); + if (!match_status.ok() && !absl::IsNotFound(match_status)) { + LOG(ERROR) << "Got error when glob-matching in " << dir << ": " + << match_status; + } else { + current_corpus_shard_fnames.insert(matched_fnames.begin(), + matched_fnames.end()); + corpus_shard_fnames.insert(corpus_shard_fnames.end(), + matched_fnames.begin(), + matched_fnames.end()); + LOG(INFO) << "Found " << matched_fnames.size() << " shard(s) matching " + << glob; + } + } + if (!source.individual_input_rel_glob.empty()) { + std::vector matched_fnames; + const std::string glob = fs::path{dir} / source.individual_input_rel_glob; + const auto match_status = RemoteGlobMatch(glob, matched_fnames); + if (!match_status.ok() && !absl::IsNotFound(match_status)) { + LOG(ERROR) << "Got error when glob-matching in " << dir << ": " + << match_status; + } else { + size_t num_added_individual_inputs = 0; + for (auto& fname : matched_fnames) { + if (current_corpus_shard_fnames.contains(fname)) continue; + if (RemotePathIsDirectory(fname)) continue; + ++num_added_individual_inputs; + individual_input_fnames.push_back(std::move(fname)); + } + LOG(INFO) << "Found " << num_added_individual_inputs + << " individual input(s) with glob " << glob; + } + } } - LOG(INFO) << "Found " << corpus_shard_fnames.size() - << " shard(s) total in source " << source.dir_glob; + LOG(INFO) << "Found " << corpus_shard_fnames.size() << " shard(s) and " + << individual_input_fnames.size() + << " individual input(s) total in source " << source.dir_glob; - if (corpus_shard_fnames.empty()) { + if (corpus_shard_fnames.empty() && individual_input_fnames.empty()) { LOG(WARNING) << "Skipping empty source " << source.dir_glob; return absl::OkStatus(); } @@ -140,10 +174,12 @@ absl::Status SampleSeedCorpusElementsFromSource( // const auto num_shards = corpus_shard_fnames.size(); std::vector src_elts_per_shard(num_shards); std::vector src_elts_with_features_per_shard(num_shards, 0); + InputAndFeaturesVec src_elts; { constexpr int kMaxReadThreads = 32; - ThreadPool threads{std::min(kMaxReadThreads, num_shards)}; + ThreadPool threads{std::min( + kMaxReadThreads, std::max(num_shards, individual_input_fnames.size()))}; for (int shard = 0; shard < num_shards; ++shard) { const auto& corpus_fname = corpus_shard_fnames[shard]; @@ -193,11 +229,27 @@ absl::Status SampleSeedCorpusElementsFromSource( // threads.Schedule(read_shard); } + + RPROF_SNAPSHOT_AND_LOG("Done reading shards"); + + src_elts.resize(individual_input_fnames.size()); + for (size_t index = 0; index < individual_input_fnames.size(); ++index) { + threads.Schedule([index, &individual_input_fnames, &src_elts] { + ByteArray input; + const auto& path = individual_input_fnames[index]; + const auto read_status = RemoteFileGetContents(path, input); + if (!read_status.ok()) { + LOG(WARNING) << "Skipping individual input path " << path + << " due to read error: " << read_status; + return; + } + src_elts[index] = {std::move(input), {}}; + }); + } } RPROF_SNAPSHOT_AND_LOG("Done reading"); - InputAndFeaturesVec src_elts; size_t src_num_features = 0; for (int s = 0; s < num_shards; ++s) { @@ -217,6 +269,15 @@ absl::Status SampleSeedCorpusElementsFromSource( // RPROF_SNAPSHOT_AND_LOG("Done merging"); + // Remove empty inputs possibly due to read errors. + auto remove_it = + std::remove_if(src_elts.begin(), src_elts.end(), + [](const auto& elt) { return std::get<0>(elt).empty(); }); + if (remove_it != src_elts.end()) { + LOG(WARNING) << "Removed " << src_elts.end() - remove_it << " empty inputs"; + src_elts.erase(remove_it, src_elts.end()); + } + LOG(INFO) << "Read total of " << src_elts.size() << " elements (" << src_num_features << " with features) from source " << source.dir_glob; diff --git a/centipede/seed_corpus_maker_lib.h b/centipede/seed_corpus_maker_lib.h index 68604fdb..5c3bb1c6 100644 --- a/centipede/seed_corpus_maker_lib.h +++ b/centipede/seed_corpus_maker_lib.h @@ -33,11 +33,16 @@ namespace centipede { // Native struct used by the seed corpus library for seed corpus source. // // TODO(b/362576261): Currently this is mirroring the `proto::SeedCorpusSource` -// proto. But in the future it may change with the core seeding API. +// proto. But in the future it may change with the core seeding API - any +// difference is commented below. struct SeedCorpusSource { std::string dir_glob; uint32_t num_recent_dirs; std::string shard_rel_glob; + // If non-empty, will be used to glob the individual input files (with one + // input in each file) in the source dirs. Any files matching `shard_rel_glob` + // will be skipped. + std::string individual_input_rel_glob; std::variant sampled_fraction_or_count; }; diff --git a/centipede/seed_corpus_maker_lib_test.cc b/centipede/seed_corpus_maker_lib_test.cc index a2e2b124..4064c3e3 100644 --- a/centipede/seed_corpus_maker_lib_test.cc +++ b/centipede/seed_corpus_maker_lib_test.cc @@ -21,13 +21,17 @@ #include // NOLINT #include #include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/log/check.h" #include "absl/strings/str_cat.h" #include "./centipede/feature.h" #include "./centipede/workdir.h" +#include "./common/defs.h" #include "./common/logging.h" // IWYU pragma: keep +#include "./common/remote_file.h" #include "./common/test_util.h" namespace centipede { @@ -36,6 +40,7 @@ namespace { namespace fs = std::filesystem; using ::testing::IsSubsetOf; +using ::testing::IsSupersetOf; inline constexpr auto kIdxDigits = WorkDir::kDigitsInShardIndex; @@ -178,5 +183,69 @@ TEST(SeedCorpusMakerLibTest, RoundTripWriteReadWrite) { } } +TEST(SeedCorpusMakerLibTest, LoadsBothIndividualInputsAndShardsFromSource) { + const fs::path test_dir = GetTestTempDir(test_info_->name()); + chdir(test_dir.c_str()); + + const InputAndFeaturesVec kShardedInputs = { + {{0}, {}}, + {{1}, {feature_domains::kNoFeature}}, + {{0, 1}, {0x11, 0x23}}, + }; + constexpr std::string_view kCovBin = "bin"; + constexpr std::string_view kCovHash = "hash"; + constexpr std::string_view kRelDir = "dir/foo"; + + const std::vector kIndividualInputs = { + {0, 1, 2}, + {0, 1, 2, 3}, + // Empty input expected to be not in the sample result. + {}}; + // Write sharded inputs. + { + constexpr size_t kNumShards = 2; + const SeedCorpusDestination destination = { + .dir_path = std::string(kRelDir), + .shard_rel_glob = absl::StrCat("distilled-", kCovBin, ".*"), + .shard_index_digits = kIdxDigits, + .num_shards = kNumShards, + }; + CHECK_OK(WriteSeedCorpusElementsToDestination( // + kShardedInputs, kCovBin, kCovHash, destination)); + const std::string workdir = (test_dir / kRelDir).c_str(); + ASSERT_NO_FATAL_FAILURE(VerifyShardsExist( // + workdir, kCovBin, kCovHash, kNumShards, ShardType::kDistilled)); + } + + // Write individual inputs + for (int i = 0; i < kIndividualInputs.size(); ++i) { + const auto path = std::filesystem::path(test_dir) / kRelDir / + absl::StrCat("individual_input_", i); + CHECK_OK(RemoteFileSetContents(path.string(), kIndividualInputs[i])); + } + + // Test that sharded and individual inputs matches what we wrote. + { + InputAndFeaturesVec elements; + ASSERT_OK(SampleSeedCorpusElementsFromSource( // + SeedCorpusSource{ + .dir_glob = std::string(kRelDir), + .num_recent_dirs = 1, + .shard_rel_glob = absl::StrCat("distilled-", kCovBin, ".*"), + // Intentionally try to match the shard files and test if they will + // be read as individual inputs. + .individual_input_rel_glob = "*", + .sampled_fraction_or_count = 1.0f, + }, + kCovBin, kCovHash, elements)); + EXPECT_EQ(elements.size(), 5); // Non-empty inputs + EXPECT_THAT(elements, IsSupersetOf(kShardedInputs)); + EXPECT_THAT(elements, IsSupersetOf(InputAndFeaturesVec{ + {{0, 1, 2}, {}}, + {{0, 1, 2, 3}, {}}, + })); + } +} + } // namespace } // namespace centipede diff --git a/common/remote_file_oss.cc b/common/remote_file_oss.cc index 2ea73afe..646d5fa6 100644 --- a/common/remote_file_oss.cc +++ b/common/remote_file_oss.cc @@ -349,6 +349,10 @@ absl::Status RemoteGlobMatch(std::string_view glob, if (int ret = ::glob(std::string{glob}.c_str(), GLOB_TILDE, HandleGlobError, &glob_ret); ret != 0) { + if (ret == GLOB_NOMATCH) { + return absl::NotFoundError(absl::StrCat( + "glob() returned NOMATCH for pattern: ", std::string(glob))); + } return absl::UnknownError(absl::StrCat( "glob() failed, pattern: ", std::string(glob), ", returned: ", ret)); } diff --git a/e2e_tests/functional_test.cc b/e2e_tests/functional_test.cc index 33225640..3e24d4a3 100644 --- a/e2e_tests/functional_test.cc +++ b/e2e_tests/functional_test.cc @@ -75,6 +75,22 @@ absl::flat_hash_map WithTestSanitizerOptions( return env; } +int CountSubstrs(absl::string_view haystack, absl::string_view needle) { + int count = 0; + while (true) { + size_t pos = haystack.find(needle); + if (pos == haystack.npos) return count; + ++count; + haystack.remove_prefix(pos + needle.size()); + } +} + +// Counts the number of times the target binary has been run. Needed because +// Centipede runs the binary multiple times. +int CountTargetRuns(absl::string_view std_err) { + return CountSubstrs(std_err, "FuzzTest functional test target run"); +} + class UnitTestModeTest : public ::testing::Test { protected: RunResults Run( @@ -128,16 +144,6 @@ TEST_F(UnitTestModeTest, UnitTestModeLimitsNumberOfIterationsByWallTime) { EXPECT_THAT(status, Eq(ExitCode(0))); } -int CountSubstrs(absl::string_view haystack, absl::string_view needle) { - int count = 0; - while (true) { - size_t pos = haystack.find(needle); - if (pos == haystack.npos) return count; - ++count; - haystack.remove_prefix(pos + needle.size()); - } -} - RE2 MakeReproducerRegex(absl::string_view suite_name, absl::string_view test_name, absl::string_view args) { return RE2( @@ -208,17 +214,16 @@ TEST_F(UnitTestModeTest, TEST_F(UnitTestModeTest, GlobalEnvironmentGoesThroughCompleteLifecycle) { auto [status, std_out, std_err] = Run("MySuite.GoogleTestExpect"); EXPECT_EQ( - 1, CountSubstrs(std_err, "<>")); - EXPECT_EQ(1, CountSubstrs(std_err, "<>")); - EXPECT_EQ(1, CountSubstrs(std_err, "<>")); - EXPECT_EQ( - 1, CountSubstrs(std_err, "<>")); + CountSubstrs(std_err, "<>"), + CountSubstrs(std_err, "<>")); + EXPECT_EQ(CountSubstrs(std_err, "<>"), + CountSubstrs(std_err, "<>")); } TEST_F(UnitTestModeTest, FixtureGoesThroughCompleteLifecycle) { auto [status, std_out, std_err] = Run("FixtureTest.NeverFails"); - EXPECT_EQ(1, CountSubstrs(std_err, "<>")); - EXPECT_EQ(1, CountSubstrs(std_err, "<>")); + EXPECT_EQ(CountSubstrs(std_err, "<>"), + CountSubstrs(std_err, "<>")); } TEST_F(UnitTestModeTest, @@ -231,19 +236,17 @@ TEST_F(UnitTestModeTest, TEST_F(UnitTestModeTest, GoogleTestPerFuzzTestFixtureInstantiatedOncePerFuzzTest) { auto [status, std_out, std_err] = - Run("CallCountPerFuzzTest.CallCountReachesAtLeastTen"); - EXPECT_EQ( - 1, CountSubstrs(std_err, "<>")); + Run("CallCountPerFuzzTest.CallCountPerFuzzTestEqualsToGlobalCount"); + EXPECT_THAT(status, Eq(ExitCode(0))); } -TEST_F(UnitTestModeTest, GoogleTestStaticTestSuiteFunctionsCalledOnce) { +TEST_F(UnitTestModeTest, GoogleTestStaticTestSuiteFunctionsCalledInBalance) { auto [status, std_out, std_err] = - Run("CallCountPerFuzzTest.CallCountReachesAtLeastTen:" + Run("CallCountPerFuzzTest.CallCountPerFuzzTestEqualsToGlobalCount:" "CallCountPerFuzzTest.NeverFails"); - EXPECT_EQ(1, - CountSubstrs(std_err, "<>")); EXPECT_EQ( - 1, CountSubstrs(std_err, "<>")); + CountSubstrs(std_err, "<>"), + CountSubstrs(std_err, "<>")); } TEST_F(UnitTestModeTest, GoogleTestWorksWithProtoExtensionsUsedInSeeds) { @@ -769,10 +772,10 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, ReproducerIsDumpedWhenEnvVarIsSet) { auto args = parsed->ToCorpus>(); EXPECT_THAT(args, Optional(FieldsAre(StartsWith("Fuzz")))) << std_err; EXPECT_THAT(std_err, - HasSubstr(absl::StrCat("Reproducer file was dumped at:\n", - replay_files[0].path))); - EXPECT_THAT(std_err, HasSubstr(absl::StrCat("--test_env=FUZZTEST_REPLAY=", - replay_files[0].path))); + AllOf(HasSubstr("Reproducer file was dumped at:"), + HasSubstr(replay_files[0].path), + HasSubstr(absl::StrCat("--test_env=FUZZTEST_REPLAY=", + replay_files[0].path)))); } TEST_F(FuzzingModeCommandLineInterfaceTest, SavesCorpusWhenEnvVarIsSet) { @@ -991,8 +994,8 @@ TEST_F(FuzzingModeCommandLineInterfaceTest, ReplayFile replay(std::in_place, std::tuple{10, 1979.125}); auto [status, std_out, std_err] = RunWith({{"fuzz", "MySuite.WithDomainClass"}}, replay.GetReplayEnv()); - EXPECT_THAT(std_err, HasSubstr("argument 0: 10")); - EXPECT_THAT(std_err, HasSubstr("argument 1: 1979.125")); + EXPECT_THAT(std_err, HasSubstr("argument 0: 10")) << std_err; + EXPECT_THAT(std_err, HasSubstr("argument 1: 1979.125")) << std_err; EXPECT_THAT(status, Ne(ExitCode(0))); } @@ -1253,16 +1256,6 @@ class FuzzingModeFixtureTest .timeout = absl::InfiniteDuration()}); } } - - // Counts the number of times the target binary has been run. Needed because - // Centipede runs the binary multiple times. - int CountTargetRuns(absl::string_view std_err) { - if (GetParam().multi_process) { - return CountSubstrs(std_err, "Centipede fuzz target runner; argv[0]:"); - } else { - return 1; - } - } }; TEST_P(FuzzingModeFixtureTest, GlobalEnvironmentIsSetUpForFailingTest) { @@ -1315,13 +1308,15 @@ TEST_P(FuzzingModeFixtureTest, TEST_P(FuzzingModeFixtureTest, GoogleTestPerFuzzTestFixtureInstantiatedOncePerFuzzTest) { auto [status, std_out, std_err] = - Run("CallCountPerFuzzTest.CallCountReachesAtLeastTen", /*iterations=*/10); - EXPECT_THAT(std_err, HasSubstr("<>")); + Run("CallCountPerFuzzTest.CallCountPerFuzzTestEqualsToGlobalCount", + /*iterations=*/10); + EXPECT_THAT(status, Eq(ExitCode(0))); } TEST_P(FuzzingModeFixtureTest, GoogleTestStaticTestSuiteFunctionsCalledOnce) { auto [status, std_out, std_err] = - Run("CallCountPerFuzzTest.CallCountReachesAtLeastTen", /*iterations=*/10); + Run("CountPerFuzzTest.CallCountPerFuzzTestEqualsToGlobalCount", + /*iterations=*/10); EXPECT_GT(CountTargetRuns(std_err), 0); EXPECT_EQ(CountTargetRuns(std_err), CountSubstrs(std_err, "<>")); diff --git a/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc b/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc index 50a1abfb..193bff96 100644 --- a/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc +++ b/e2e_tests/testdata/fuzz_tests_for_functional_testing.cc @@ -65,6 +65,11 @@ using ::fuzztest::internal::TestProtobufWithRequired; using ::fuzztest::internal::TestSubProtobuf; using ::google::protobuf::FieldDescriptor; +bool print_target_run_message_once = []() { + fputs("FuzzTest functional test target run\n", stderr); + return true; +}(); + void PassesWithPositiveInput(int x) { if (x <= 0) std::abort(); } @@ -757,12 +762,18 @@ FUZZ_TEST_F(AlternateSignalStackFixture, void DetectRegressionAndCoverageInputs(const std::string& input) { if (absl::StartsWith(input, "regression")) { - std::cout << "regression input detected: " << input << std::endl; + std::cerr << "regression input detected: " << input << std::endl; } if (absl::StartsWith(input, "coverage")) { - std::cout << "coverage input detected: " << input << std::endl; + std::cerr << "coverage input detected: " << input << std::endl; + // Sleep for the first coverage input for depleteing the + // replay time budget. + static bool first = true; + if (first) { + first = false; + absl::SleepFor(absl::Seconds(2)); + } } - absl::SleepFor(absl::Seconds(0.1)); } FUZZ_TEST(MySuite, DetectRegressionAndCoverageInputs); diff --git a/e2e_tests/testdata/fuzz_tests_using_googletest.cc b/e2e_tests/testdata/fuzz_tests_using_googletest.cc index d2d01caf..5b3dea92 100644 --- a/e2e_tests/testdata/fuzz_tests_using_googletest.cc +++ b/e2e_tests/testdata/fuzz_tests_using_googletest.cc @@ -88,19 +88,18 @@ class CallCountPerIteration FUZZ_TEST_F(CallCountPerIteration, CallCountIsAlwaysIncrementedFromInitialValue); +static int global_call_count = 0; class CallCountPerFuzzTest : public ::fuzztest::PerFuzzTestFixtureAdapter { public: - void CallCountReachesAtLeastTen(int) { - if (call_count_ < std::numeric_limits::max()) ++call_count_; - if (call_count_ == 10) { - fprintf(stderr, "<>\n", - call_count_); - } + void CallCountPerFuzzTestEqualsToGlobalCount(int) { + ++call_count_; + ++global_call_count; + EXPECT_EQ(call_count_, global_call_count); } void NeverFails(int) {} }; -FUZZ_TEST_F(CallCountPerFuzzTest, CallCountReachesAtLeastTen); +FUZZ_TEST_F(CallCountPerFuzzTest, CallCountPerFuzzTestEqualsToGlobalCount); FUZZ_TEST_F(CallCountPerFuzzTest, NeverFails); TEST(SharedSuite, WorksAsUnitTest) {} diff --git a/fuzztest/internal/centipede_adaptor.cc b/fuzztest/internal/centipede_adaptor.cc index 292e450c..c8a0e46a 100644 --- a/fuzztest/internal/centipede_adaptor.cc +++ b/fuzztest/internal/centipede_adaptor.cc @@ -479,7 +479,7 @@ class CentipedeFixtureDriver : public UntypedFixtureDriver { if (runtime_.skipping_requested()) { CentipedeSetExecutionResult(nullptr, 0); } - if (!runner_mode) CentipedeFinalizeProcessing(); + CentipedeFinalizeProcessing(); } void TearDownFuzzTest() override { orig_fixture_driver_->TearDownFuzzTest(); }