When in fuzzing mode, if a non-fatal failure is found (eg like a failed EXPECT_EQ) try to minimize the sample in-process before triggering a report. PiperOrigin-RevId: 506363317
diff --git a/e2e_tests/functional_test.cc b/e2e_tests/functional_test.cc index 3af3604..e4b5694 100644 --- a/e2e_tests/functional_test.cc +++ b/e2e_tests/functional_test.cc
@@ -1249,5 +1249,15 @@ 1, CountSubstrs(std_err, "<<CallCountGoogleTest::TearDownTestSuite()>>")); } +TEST_F(FuzzingModeTest, NonFatalFailureAllowsMinimization) { + auto [status, std_out, std_err] = + RunWith("--fuzz=MySuite.NonFatalFailureAllowsMinimization"); + // The final failure should be with the known minimal result, even though many + // "larger" inputs also trigger the failure. + EXPECT_THAT(std_err, HasSubstr("argument 0: \"0123\"")); + + EXPECT_THAT(status.Signal(), Eq(SIGABRT)); +} + } // namespace } // namespace fuzztest::internal
diff --git a/e2e_tests/testdata/fuzz_tests_using_googletest.cc b/e2e_tests/testdata/fuzz_tests_using_googletest.cc index 9f799af..86ca808 100644 --- a/e2e_tests/testdata/fuzz_tests_using_googletest.cc +++ b/e2e_tests/testdata/fuzz_tests_using_googletest.cc
@@ -23,6 +23,7 @@ #include "gtest/gtest.h" #include "./fuzztest/fuzztest.h" #include "./fuzztest/googletest_fixture_adapter.h" +#include "./fuzztest/internal/test_protobuf.pb.h" namespace { @@ -106,4 +107,15 @@ void WorksAsFuzzTest(int) {} FUZZ_TEST(SharedSuite, WorksAsFuzzTest); +void NonFatalFailureAllowsMinimization(const std::string& str) { + // Make very fuzz predicate that would fail on a large number of values, but + // there is one very specific minimum. + if (str.size() < 4 || str[0] < '0' || str[1] < '1' || str[2] < '2' || + str[3] <= str[2]) { + return; + } + ADD_FAILURE() << str; +} +FUZZ_TEST(MySuite, NonFatalFailureAllowsMinimization); + } // namespace
diff --git a/fuzztest/fuzztest_gtest_main.cc b/fuzztest/fuzztest_gtest_main.cc index cc57696..984bac4 100644 --- a/fuzztest/fuzztest_gtest_main.cc +++ b/fuzztest/fuzztest_gtest_main.cc
@@ -64,7 +64,7 @@ const bool is_duration_specified = absl::ZeroDuration() < duration && duration < absl::InfiniteDuration(); if (is_duration_specified) { - fuzztest::internal::fuzz_time_limit = duration; + fuzztest::internal::Runtime::instance().SetFuzzTimeLimit(duration); } if (is_fuzz_specified || is_duration_specified) { GOOGLEFUZZTEST_REGISTER_FOR_GOOGLETEST(fuzztest::RunMode::kFuzz, &argc,
diff --git a/fuzztest/googletest_adaptor.h b/fuzztest/googletest_adaptor.h index ad93fbc..9b57d80 100644 --- a/fuzztest/googletest_adaptor.h +++ b/fuzztest/googletest_adaptor.h
@@ -43,7 +43,7 @@ ::testing::UnitTest::GetInstance()->listeners().Append( \ new ::fuzztest::internal::GTest_EventListener< \ ::testing::EmptyTestEventListener, ::testing::TestPartResult>()), \ - ::fuzztest::internal::run_mode = selected_run_mode) + ::fuzztest::internal::Runtime::instance().SetRunMode(selected_run_mode)) namespace fuzztest::internal { @@ -54,7 +54,7 @@ void TestBody() override { auto test = test_.make(); - if (run_mode == RunMode::kUnitTest) { + if (Runtime::instance().run_mode() == RunMode::kUnitTest) { test->RunInUnitTestMode(); } else { ASSERT_EQ(0, test->RunInFuzzingMode(argc_, argv_)) << "Fuzzing failure."; @@ -84,14 +84,17 @@ public: void OnTestPartResult(const TestPartResult& test_part_result) override { if (!test_part_result.failed()) return; - if (run_mode == RunMode::kFuzz) { - // The SIGABRT will trigger a report. - std::abort(); + Runtime& runtime = Runtime::instance(); + if (runtime.run_mode() == RunMode::kFuzz) { + if (runtime.should_terminate_on_non_fatal_failure()) { + // The SIGABRT will trigger a report. + std::abort(); + } } else { // Otherwise, we report it manually. - on_failure.PrintReportOnDefaultSink(); + runtime.PrintReportOnDefaultSink(); } - external_failure_was_detected.store(true, std::memory_order_relaxed); + runtime.SetExternalFailureDetected(true); } };
diff --git a/fuzztest/internal/domain.h b/fuzztest/internal/domain.h index 859e06b..f2e8d4a 100644 --- a/fuzztest/internal/domain.h +++ b/fuzztest/internal/domain.h
@@ -990,7 +990,8 @@ const bool can_shrink = val.size() > this->min_size_; const bool can_grow = !only_shrink && val.size() < this->max_size_; const bool can_change = val.size() != 0; - const bool can_use_memory_dict = container_has_memory_dict && can_change && + const bool can_use_memory_dict = !only_shrink && + container_has_memory_dict && can_change && GetExecutionCoverage() != nullptr; const int action_count =
diff --git a/fuzztest/internal/runtime.cc b/fuzztest/internal/runtime.cc index c0b3100..c9c3ccd 100644 --- a/fuzztest/internal/runtime.cc +++ b/fuzztest/internal/runtime.cc
@@ -61,14 +61,9 @@ namespace fuzztest::internal { -RunMode run_mode = RunMode::kUnitTest; -ABSL_CONST_INIT absl::Duration fuzz_time_limit = absl::InfiniteDuration(); -std::atomic<bool> external_failure_was_detected; -std::atomic<bool> termination_requested; -OnFailure on_failure; void (*crash_handler_hook)(); -void OnFailure::DumpReproducer(std::string_view outdir) const { +void Runtime::DumpReproducer(std::string_view outdir) const { const std::string content = current_args_->domain.UntypedSerializeCorpus(current_args_->corpus_value) .ToString(); @@ -82,7 +77,7 @@ } } -void OnFailure::PrintFinalStats(absl::FormatRawSink out) const { +void Runtime::PrintFinalStats(absl::FormatRawSink out) const { const std::string separator = '\n' + std::string(65, '=') + '\n'; absl::Format(out, "%s=== Fuzzing stats\n\n", separator); @@ -95,10 +90,10 @@ absl::Format(out, "Corpus size: %d\n", stats_->useful_inputs); } -void OnFailure::PrintReport(absl::FormatRawSink out) const { +void Runtime::PrintReport(absl::FormatRawSink out) const { // We don't want to try and print a fuzz report when we are not running a fuzz // test, even if we got a crash. - if (!enabled_) return; + if (!reporter_enabled_) return; if (crash_handler_hook) crash_handler_hook(); @@ -110,7 +105,7 @@ } } - if (run_mode != RunMode::kUnitTest) { + if (run_mode() != RunMode::kUnitTest) { PrintFinalStats(out); } @@ -118,8 +113,9 @@ if (current_args_ != nullptr) { absl::Format(out, "%s=== BUG FOUND!\n\n", separator); - absl::Format(out, "%s:%d: Counterexample found for %s.%s.\n", test_->file(), - test_->line(), test_->suite_name(), test_->test_name()); + absl::Format(out, "%s:%d: Counterexample found for %s.%s.\n", + current_test_->file(), current_test_->line(), + current_test_->suite_name(), current_test_->test_name()); absl::Format(out, "The test fails with input:\n"); const int num_args = current_args_->domain.UntypedPrintCorpusValue( current_args_->corpus_value, out, PrintMode::kHumanReadable, -1); @@ -133,10 +129,10 @@ // There doesn't seem to be a good way to generate a reproducer test when // the test uses a fixture (see b/241271658). - if (!test_->uses_fixture()) { + if (!current_test_->uses_fixture()) { absl::Format(out, "%s=== Reproducer test\n\n", separator); absl::Format(out, "TEST(%1$s, %2$sRegression) {\n %2$s(\n", - test_->suite_name(), test_->test_name()); + current_test_->suite_name(), current_test_->test_name()); for (size_t i = 0; i < num_args; ++i) { if (i != 0) absl::Format(out, ",\n"); absl::Format(out, " "); @@ -148,8 +144,9 @@ } } else { absl::Format(out, "%s=== SETUP FAILURE!\n\n", separator); - absl::Format(out, "%s:%d: There was a problem with %s.%s.", test_->file(), - test_->line(), test_->suite_name(), test_->test_name()); + absl::Format(out, "%s:%d: There was a problem with %s.%s.", + current_test_->file(), current_test_->line(), + current_test_->suite_name(), current_test_->test_name()); if (test_abort_message != nullptr) { absl::Format(out, "%s", *test_abort_message); } @@ -191,7 +188,7 @@ if (!old_handler || signum != SIGTRAP || (info->si_code != TRAP_PERF && info->si_code != SI_TIMER)) { // Dump our info first. - on_failure.PrintReport(&signal_out_sink); + Runtime::instance().PrintReport(&signal_out_sink); // The old signal handler might print important messages (e.g., strack // trace) to the original file descriptors, therefore we restore them before // calling them. @@ -204,7 +201,7 @@ } static void HandleTermination(int, siginfo_t*, void*) { - termination_requested.store(true, std::memory_order_relaxed); + Runtime::instance().SetTerminationRequested(); } static void SetNewSigAction(int signum, void (*handler)(int, siginfo_t*, void*), @@ -238,7 +235,7 @@ // after printing its output. This handler helps us print our output // afterwards. __sanitizer_set_death_callback( - [](auto...) { on_failure.PrintReport(&signal_out_sink); }); + [](auto...) { Runtime::instance().PrintReport(&signal_out_sink); }); #endif for (OldSignalHandler& h : crash_handlers) { @@ -250,11 +247,11 @@ } } -void OnFailure::PrintFinalStatsOnDefaultSink() const { +void Runtime::PrintFinalStatsOnDefaultSink() const { PrintFinalStats(&signal_out_sink); } -void OnFailure::PrintReportOnDefaultSink() const { +void Runtime::PrintReportOnDefaultSink() const { PrintReport(&signal_out_sink); } @@ -262,9 +259,9 @@ // TODO(sbenzaquen): We should still install signal handlers in other systems. void InstallSignalHandlers(FILE* out) {} -void OnFailure::PrintFinalStatsOnDefaultSink() const {} +void Runtime::PrintFinalStatsOnDefaultSink() const {} -void OnFailure::PrintReportOnDefaultSink() const {} +void Runtime::PrintReportOnDefaultSink() const {} #endif // __linux__ using corpus_type = GenericDomainCorpusType; @@ -289,7 +286,9 @@ absl::discrete_distribution<>(weights.begin(), weights.end()); } -FuzzTestFuzzerImpl::~FuzzTestFuzzerImpl() { on_failure.Disable(); } +FuzzTestFuzzerImpl::~FuzzTestFuzzerImpl() { + Runtime::instance().DisableReporter(); +} std::optional<corpus_type> FuzzTestFuzzerImpl::TryParse(std::string_view data) { if (auto parsed = IRObject::FromString(data)) { @@ -299,7 +298,7 @@ } bool FuzzTestFuzzerImpl::ReplayInputsIfAvailable() { - run_mode = RunMode::kFuzz; + runtime_.SetRunMode(RunMode::kFuzz); if (const auto replay_corpus = ReadReplayFile()) { for (const auto& corpus_value : *replay_corpus) { @@ -433,6 +432,11 @@ FuzzTestFuzzerImpl::RunResult FuzzTestFuzzerImpl::TrySample( const Input& sample, bool write_to_file) { RunResult run_result = RunOneInput(sample); + if (runtime_.external_failure_detected()) { + // We detected a non fatal failure. Record it separately to minimize it + // locally. + minimal_non_fatal_counterexample_ = sample; + } if (!run_result.new_coverage) return run_result; if (write_to_file) TryWriteCorpusFile(sample); @@ -539,7 +543,7 @@ if (runs_limit_.has_value() && stats_.runs >= *runs_limit_) return true; if (time_limit_ != absl::InfiniteFuture() && absl::Now() > time_limit_) return true; - return termination_requested.load(std::memory_order_relaxed); + return runtime_.termination_requested(); } void FuzzTestFuzzerImpl::PopulateFromSeeds() { @@ -552,8 +556,8 @@ void FuzzTestFuzzerImpl::RunInUnitTestMode() { fixture_driver_->SetUpFuzzTest(); [&] { - on_failure.Enable(&stats_, [] { return absl::Now(); }); - on_failure.SetCurrentTest(&test_); + runtime_.EnableReporter(&stats_, [] { return absl::Now(); }); + runtime_.SetCurrentTest(&test_); // TODO(sbenzaquen): Currently, some infrastructure code assumes that replay // works in unit test mode, so we support it. However, we would like to @@ -563,11 +567,11 @@ if (ReplayInputsIfAvailable()) { // If ReplayInputs returns, it means the replay didn't crash. // In replay mode, we only replay. - on_failure.Disable(); + runtime_.DisableReporter(); return; } - run_mode = RunMode::kUnitTest; + runtime_.SetRunMode(RunMode::kUnitTest); PopulateFromSeeds(); @@ -576,9 +580,9 @@ Input mutation{params_domain_->UntypedInit(prng)}; constexpr size_t max_iterations = 10000; for (int i = 0; i < max_iterations; ++i) { - external_failure_was_detected.store(false, std::memory_order_relaxed); + runtime_.SetExternalFailureDetected(false); RunOneInput(mutation); - if (external_failure_was_detected.load(std::memory_order_relaxed)) { + if (runtime_.external_failure_detected()) { break; } // We mutate the value, except that every num_mutations_per_value we @@ -597,7 +601,7 @@ break; } } - on_failure.SetCurrentTest(nullptr); + runtime_.SetCurrentTest(nullptr); }(); fixture_driver_->TearDownFuzzTest(); } @@ -606,8 +610,8 @@ const Input& input) { ++stats_.runs; auto untyped_args = params_domain_->UntypedGetValue(input.args); - OnFailure::Args debug_args{input.args, *params_domain_}; - on_failure.SetCurrentArgs(&debug_args); + Runtime::Args debug_args{input.args, *params_domain_}; + runtime_.SetCurrentArgs(&debug_args); // Reset and observe the coverage map and start tracing in // the tightest scope possible. In particular, we can't include the call @@ -634,19 +638,58 @@ if (execution_coverage_ != nullptr) { new_coverage = corpus_coverage_.Update(execution_coverage_); } - on_failure.UnsetCurrentArgs(); + runtime_.UnsetCurrentArgs(); return {new_coverage, run_time}; } +void FuzzTestFuzzerImpl::MinimizeNonFatalFailureLocally(absl::BitGenRef prng) { + // We try to minimize the counterexample until we reach a point where no new + // failures are found. + // We stop when run kMaxTriedWithoutFailure consecutive runs without finding a + // smaller failure, but also add a time limit in case each iteration takes too + // long. + const absl::Time deadline = + std::min(absl::Now() + absl::Minutes(1), time_limit_); + int tries_without_failure = 0; + constexpr int kMaxTriedWithoutFailure = 10000; + while (tries_without_failure < kMaxTriedWithoutFailure && + absl::Now() < deadline) { + auto copy = *minimal_non_fatal_counterexample_; + // Mutate a random number of times, in case one is not enough to + // reach another failure, but prefer a low number of mutations (thus Zipf). + for (int num_mutations = absl::Zipf(prng, 10); num_mutations >= 0; + --num_mutations) { + params_domain_->UntypedMutate(copy.args, prng, /* only_shrink= */ true); + } + // Only run it if it actually is different. Random mutations might + // not actually change the value, or we have reached a minimum that can't be + // minimized anymore. + if (params_domain_ + ->UntypedSerializeCorpus(minimal_non_fatal_counterexample_->args) + .ToString() != + params_domain_->UntypedSerializeCorpus(copy.args).ToString()) { + runtime_.SetExternalFailureDetected(false); + RunOneInput(copy); + if (runtime_.external_failure_detected()) { + // Found a smaller one, record it and reset the counter. + minimal_non_fatal_counterexample_ = std::move(copy); + tries_without_failure = 0; + continue; + } + } + ++tries_without_failure; + } +} + int FuzzTestFuzzerImpl::RunInFuzzingMode(int* /*argc*/, char*** /*argv*/) { fixture_driver_->SetUpFuzzTest(); const int exit_code = [&] { - run_mode = RunMode::kFuzz; + runtime_.SetRunMode(RunMode::kFuzz); if (IsSilenceTargetEnabled()) SilenceTargetStdoutAndStderr(); - on_failure.Enable(&stats_, [] { return absl::Now(); }); - on_failure.SetCurrentTest(&test_); + runtime_.EnableReporter(&stats_, [] { return absl::Now(); }); + runtime_.SetCurrentTest(&test_); if (ReplayInputsIfAvailable()) { // If ReplayInputs returns, it means the replay didn't crash. @@ -696,12 +739,13 @@ } } - if (fuzz_time_limit != absl::InfiniteDuration()) { + if (runtime_.fuzz_time_limit() != absl::InfiniteDuration()) { absl::FPrintF(GetStderr(), "[.] Fuzzing timeout set to: %s\n", - absl::FormatDuration(fuzz_time_limit)); - time_limit_ = stats_.start_time + fuzz_time_limit; + absl::FormatDuration(runtime_.fuzz_time_limit())); + time_limit_ = stats_.start_time + runtime_.fuzz_time_limit(); } + runtime_.SetShouldTerminateOnNonFatalFailure(false); // Fuzz corpus elements in round robin fashion. while (!ShouldStop()) { Input input_to_mutate = [&]() -> Input { @@ -723,11 +767,24 @@ Input mutation = input_to_mutate; MutateValue(mutation, prng); TrySampleAndUpdateInMemoryCorpus(std::move(mutation)); + + if (minimal_non_fatal_counterexample_.has_value()) { + // We found a failure, let's minimize it here. + MinimizeNonFatalFailureLocally(prng); + // Once we have minimized enough, let it crash with the best sample we + // got. + // TODO(sbenzaquen): Consider a different approach where we don't retry + // the failing sample to force a crash. Instead, we could store the + // information from the first failure and generate a report manually. + runtime_.SetShouldTerminateOnNonFatalFailure(true); + runtime_.SetExternalFailureDetected(false); + RunOneInput(*minimal_non_fatal_counterexample_); + } } } absl::FPrintF(GetStderr(), "\n[.] Fuzzing was terminated.\n"); - on_failure.PrintFinalStatsOnDefaultSink(); + runtime_.PrintFinalStatsOnDefaultSink(); absl::FPrintF(GetStderr(), "\n"); return 0; }();
diff --git a/fuzztest/internal/runtime.h b/fuzztest/internal/runtime.h index d493581..4385fce 100644 --- a/fuzztest/internal/runtime.h +++ b/fuzztest/internal/runtime.h
@@ -113,40 +113,62 @@ void InstallSignalHandlers(FILE* report_out); -// Some failures are not necessarily detected by signal handlers or by -// sanitizers. For example, we could have test framework failures like -// `EXPECT_EQ` failures from GoogleTest. -// If such a failure is detected, the external system can set -// `external_failure_was_detected` to true to bubble it up. -// Note: Even though failures should happen within the code under test, they -// could be set from other threads at any moment. We make it an atomic to avoid -// a race condition. -extern std::atomic<bool> external_failure_was_detected; - -// If true, fuzzing should terminate as soon as possible. -// Atomic because it is set from signal handlers. -extern std::atomic<bool> termination_requested; - -extern RunMode run_mode; -extern absl::Duration fuzz_time_limit; - -class OnFailure { +// This class encapsulates the runtime state that is global by necessity. +// The state is accessed by calling `Runtime::instance()`, which handles the +// necessary initialization steps. +class Runtime { public: - void Enable(const RuntimeStats* stats, absl::Time (*clock_fn)()) { - enabled_ = true; + static Runtime& instance() { + static auto* runtime = new Runtime(); + return *runtime; + } + + void SetExternalFailureDetected(bool v) { + external_failure_was_detected_.store(v, std::memory_order_relaxed); + } + bool external_failure_detected() const { + return external_failure_was_detected_.load(std::memory_order_relaxed); + } + + void SetShouldTerminateOnNonFatalFailure(bool v) { + should_terminate_on_non_fatal_failure_ = v; + } + + bool should_terminate_on_non_fatal_failure() const { + return should_terminate_on_non_fatal_failure_; + } + + void SetTerminationRequested() { + termination_requested_.store(true, std::memory_order_relaxed); + } + + bool termination_requested() const { + return termination_requested_.load(std::memory_order_relaxed); + } + + void SetRunMode(RunMode run_mode) { run_mode_ = run_mode; } + RunMode run_mode() const { return run_mode_; } + + void SetFuzzTimeLimit(absl::Duration fuzz_time_limit) { + fuzz_time_limit_ = fuzz_time_limit; + } + absl::Duration fuzz_time_limit() const { return fuzz_time_limit_; } + + void EnableReporter(const RuntimeStats* stats, absl::Time (*clock_fn)()) { + reporter_enabled_ = true; stats_ = stats; clock_fn_ = clock_fn; // In case we have not installed them yet, do so now. InstallSignalHandlers(GetStderr()); } - void Disable() { enabled_ = false; } + void DisableReporter() { reporter_enabled_ = false; } struct Args { const GenericDomainCorpusType& corpus_value; UntypedDomainInterface& domain; }; - void SetCurrentTest(const FuzzTest* test) { test_ = test; } + void SetCurrentTest(const FuzzTest* test) { current_test_ = test; } void SetCurrentArgs(Args* args) { current_args_ = args; } void UnsetCurrentArgs() { current_args_ = nullptr; } @@ -157,15 +179,38 @@ void PrintReportOnDefaultSink() const; private: + Runtime() = default; + void DumpReproducer(std::string_view outdir) const; - bool enabled_ = false; - Args* current_args_; - const FuzzTest* test_; - const RuntimeStats* stats_; + // Some failures are not necessarily detected by signal handlers or by + // sanitizers. For example, we could have test framework failures like + // `EXPECT_EQ` failures from GoogleTest. + // If such a failure is detected, the external system can set + // `external_failure_was_detected` to true to bubble it up. + // Note: Even though failures should happen within the code under test, they + // could be set from other threads at any moment. We make it an atomic to + // avoid a race condition. + std::atomic<bool> external_failure_was_detected_{false}; + + // To support in-process minimization for non-fatal failures we signal + // suppress termination until we believe minimization is complete. + bool should_terminate_on_non_fatal_failure_ = true; + + // If true, fuzzing should terminate as soon as possible. + // Atomic because it is set from signal handlers. + std::atomic<bool> termination_requested_{false}; + + RunMode run_mode_ = RunMode::kUnitTest; + absl::Duration fuzz_time_limit_ = absl::InfiniteDuration(); + + bool reporter_enabled_ = false; + Args* current_args_ = nullptr; + const FuzzTest* current_test_ = nullptr; + const RuntimeStats* stats_ = nullptr; absl::Time (*clock_fn_)() = nullptr; }; -extern OnFailure on_failure; + extern void (*crash_handler_hook)(); template <typename Arg, size_t I, typename Tuple> @@ -223,6 +268,8 @@ void UpdateCorpusDistribution(); + void MinimizeNonFatalFailureLocally(absl::BitGenRef prng); + // Runs on `sample` and returns new coverage and run time. If there's new // coverage, outputs updated runtime stats. Additionally, if `write_to_file` // is true, tries to write the sample to a file. @@ -265,6 +312,9 @@ RuntimeStats stats_{}; std::optional<size_t> runs_limit_; absl::Time time_limit_ = absl::InfiniteFuture(); + std::optional<Input> minimal_non_fatal_counterexample_; + + Runtime& runtime_ = Runtime::instance(); #ifdef FUZZTEST_COMPATIBILITY_MODE friend class FuzzTestExternalEngineAdaptor;
diff --git a/fuzztest/internal/runtime_test.cc b/fuzztest/internal/runtime_test.cc index 1d35316..5c0f6b4 100644 --- a/fuzztest/internal/runtime_test.cc +++ b/fuzztest/internal/runtime_test.cc
@@ -28,9 +28,10 @@ namespace { TEST(OnFailureTest, Output) { - const auto get_failure = [] { + auto& runtime = Runtime::instance(); + const auto get_failure = [&] { std::string s; - on_failure.PrintReport(&s); + runtime.PrintReport(&s); return s; }; // Disabled by default. @@ -39,14 +40,14 @@ FuzzTest test({"SUITE_NAME", "TEST_NAME", "FILE", 123}, nullptr); std::tuple args(17, std::string("ABC")); const RuntimeStats stats = {absl::FromUnixNanos(0), 1, 2, 3, 4}; - on_failure.Enable(&stats, [] { return absl::FromUnixNanos(1979); }); - run_mode = RunMode::kFuzz; + runtime.EnableReporter(&stats, [] { return absl::FromUnixNanos(1979); }); + runtime.SetRunMode(RunMode::kFuzz); auto domain = TupleOf(Arbitrary<int>(), Arbitrary<std::string>()); GenericDomainCorpusType generic_args( std::in_place_type<std::tuple<int, std::string>>, args); - OnFailure::Args debug_args{generic_args, domain}; - on_failure.SetCurrentTest(&test); - on_failure.SetCurrentArgs(&debug_args); + Runtime::Args debug_args{generic_args, domain}; + runtime.SetCurrentTest(&test); + runtime.SetCurrentArgs(&debug_args); EXPECT_EQ(get_failure(), R"( ================================================================= === Fuzzing stats @@ -78,7 +79,7 @@ ================================================================= )"); - on_failure.Disable(); + runtime.DisableReporter(); EXPECT_EQ(get_failure(), ""); }