No public description

PiperOrigin-RevId: 767185345
diff --git a/centipede/centipede_interface.cc b/centipede/centipede_interface.cc
index ad2b459..0e048ec 100644
--- a/centipede/centipede_interface.cc
+++ b/centipede/centipede_interface.cc
@@ -430,6 +430,11 @@
     CentipedeCallbacksFactory &callbacks_factory) {
   env.UpdateWithTargetConfig(fuzztest_config);
 
+  CHECK(fuzztest_config.fuzz_tests_in_current_shard.size() == 1)
+      << "Update corpus database must be used with single fuzz test";
+  const std::string fuzz_test_to_run =
+      fuzztest_config.fuzz_tests_in_current_shard[0];
+
   absl::Time start_time = absl::Now();
   LOG(INFO) << "Starting the update of the corpus database for fuzz tests:"
             << "\nBinary: " << env.binary
@@ -455,20 +460,6 @@
         absl::FormatTime("%Y-%m-%d-%H-%M-%S", absl::Now(), absl::UTCTimeZone());
     return stamp;
   }();
-  std::vector<std::string> fuzz_tests_to_run;
-  if (env.fuzztest_single_test_mode) {
-    CHECK(fuzztest_config.fuzz_tests_in_current_shard.size() == 1)
-        << "Must select exactly one fuzz test when running in the single test "
-           "mode";
-    fuzz_tests_to_run = fuzztest_config.fuzz_tests_in_current_shard;
-  } else {
-    for (int i = 0; i < fuzztest_config.fuzz_tests.size(); ++i) {
-      if (i % total_test_shards == test_shard_index) {
-        fuzz_tests_to_run.push_back(fuzztest_config.fuzz_tests[i]);
-      }
-    }
-  }
-  LOG(INFO) << "Fuzz tests to run:" << absl::StrJoin(fuzz_tests_to_run, ", ");
 
   const bool is_workdir_specified = !env.workdir.empty();
   CHECK(!is_workdir_specified || env.fuzztest_single_test_mode);
@@ -494,181 +485,161 @@
 
   // Step 2: Iterate over the fuzz tests and run them.
   const std::string binary = env.binary;
-  for (int i = 0; i < fuzz_tests_to_run.size(); ++i) {
-    // Clean up previous stop requests. stop_time will be set later.
-    ClearEarlyStopRequestAndSetStopTime(/*stop_time=*/absl::InfiniteFuture());
-    if (!env.fuzztest_single_test_mode &&
-        fuzztest_config.GetTimeLimitPerTest() < absl::InfiniteDuration()) {
-      const absl::Duration test_time_limit =
-          fuzztest_config.GetTimeLimitPerTest();
-      const absl::Status has_enough_time = VerifyBazelHasEnoughTimeToRunTest(
-          start_time, test_time_limit,
-          /*executed_tests_in_shard=*/i, fuzztest_config.fuzz_tests.size());
-      CHECK_OK(has_enough_time)
-          << "Not enough time for running the fuzz test "
-          << fuzz_tests_to_run[i] << " for " << test_time_limit;
-    }
-    if (!is_workdir_specified) {
-      env.workdir = base_workdir_path / fuzz_tests_to_run[i];
-    }
-    const auto execution_id_path =
-        (base_workdir_path /
-         absl::StrCat(fuzz_tests_to_run[i], ".execution_id"))
-            .string();
 
-    bool is_resuming = false;
-    if (!is_workdir_specified && fuzztest_config.execution_id.has_value()) {
-      // Use the execution IDs to resume or skip tests.
-      const bool execution_id_matched = [&] {
-        if (!RemotePathExists(execution_id_path)) return false;
-        CHECK(!RemotePathIsDirectory(execution_id_path));
-        std::string prev_execution_id;
-        CHECK_OK(RemoteFileGetContents(execution_id_path, prev_execution_id));
-        return prev_execution_id == *fuzztest_config.execution_id;
-      }();
-      if (execution_id_matched) {
-        // If execution IDs match but the previous coverage is missing, it means
-        // the test was previously finished, and we skip running for the test.
-        if (!RemotePathExists(WorkDir{env}.CoverageDirPath())) {
-          LOG(INFO) << "Skipping running the fuzz test "
-                    << fuzz_tests_to_run[i];
-          continue;
-        }
-        // If execution IDs match and the previous coverage exists, it means
-        // the same workflow got interrupted when running the test. So we resume
-        // the test.
-        is_resuming = true;
-        LOG(INFO) << "Resuming running the fuzz test " << fuzz_tests_to_run[i];
-      } else {
-        // If the execution IDs mismatch, we start a new run.
-        is_resuming = false;
-        LOG(INFO) << "Starting a new run of the fuzz test "
-                  << fuzz_tests_to_run[i];
+  // Clean up previous stop requests. stop_time will be set later.
+  ClearEarlyStopRequestAndSetStopTime(/*stop_time=*/absl::InfiniteFuture());
+  if (!is_workdir_specified) {
+    env.workdir = base_workdir_path / fuzz_test_to_run;
+  }
+  const auto execution_id_path =
+      (base_workdir_path / absl::StrCat(fuzz_test_to_run, ".execution_id"))
+          .string();
+
+  bool is_resuming = false;
+  if (!is_workdir_specified && fuzztest_config.execution_id.has_value()) {
+    // Use the execution IDs to resume or skip tests.
+    const bool execution_id_matched = [&] {
+      if (!RemotePathExists(execution_id_path)) return false;
+      CHECK(!RemotePathIsDirectory(execution_id_path));
+      std::string prev_execution_id;
+      CHECK_OK(RemoteFileGetContents(execution_id_path, prev_execution_id));
+      return prev_execution_id == *fuzztest_config.execution_id;
+    }();
+    if (execution_id_matched) {
+      // If execution IDs match but the previous coverage is missing, it means
+      // the test was previously finished, and we skip running for the test.
+      if (!RemotePathExists(WorkDir{env}.CoverageDirPath())) {
+        LOG(INFO) << "Skipping running the fuzz test " << fuzz_test_to_run;
+        return EXIT_SUCCESS;
       }
+      // If execution IDs match and the previous coverage exists, it means
+      // the same workflow got interrupted when running the test. So we resume
+      // the test.
+      is_resuming = true;
+      LOG(INFO) << "Resuming running the fuzz test " << fuzz_test_to_run;
+    } else {
+      // If the execution IDs mismatch, we start a new run.
+      is_resuming = false;
+      LOG(INFO) << "Starting a new run of the fuzz test " << fuzz_test_to_run;
     }
-    if (RemotePathExists(env.workdir) && !is_resuming) {
-      // This could be a workdir from a failed run that used a different version
-      // of the binary. We delete it so that we don't have to deal with
-      // the assumptions under which it is safe to reuse an old workdir.
+  }
+  if (RemotePathExists(env.workdir) && !is_resuming) {
+    // This could be a workdir from a failed run that used a different version
+    // of the binary. We delete it so that we don't have to deal with
+    // the assumptions under which it is safe to reuse an old workdir.
+    CHECK_OK(RemotePathDelete(env.workdir, /*recursively=*/true));
+  }
+  const WorkDir workdir{env};
+  CHECK_OK(RemoteMkdir(
+      workdir.CoverageDirPath()));  // Implicitly creates the workdir
+
+  // Updating execution ID must be after creating the coverage dir. Otherwise
+  // if it fails to create coverage dir after updating execution ID, next
+  // attempt would skip this test.
+  if (!is_workdir_specified && fuzztest_config.execution_id.has_value() &&
+      !is_resuming) {
+    CHECK_OK(RemoteFileSetContents(execution_id_path,
+                                   *fuzztest_config.execution_id));
+  }
+
+  absl::Cleanup clean_up_workdir = [is_workdir_specified, &env] {
+    if (!is_workdir_specified) {
       CHECK_OK(RemotePathDelete(env.workdir, /*recursively=*/true));
     }
-    const WorkDir workdir{env};
-    CHECK_OK(RemoteMkdir(
-        workdir.CoverageDirPath()));  // Implicitly creates the workdir
+  };
 
-    // Updating execution ID must be after creating the coverage dir. Otherwise
-    // if it fails to create coverage dir after updating execution ID, next
-    // attempt would skip this test.
-    if (!is_workdir_specified && fuzztest_config.execution_id.has_value() &&
-        !is_resuming) {
-      CHECK_OK(RemoteFileSetContents(execution_id_path,
-                                     *fuzztest_config.execution_id));
-    }
+  const std::filesystem::path fuzztest_db_path =
+      corpus_database_path / fuzz_test_to_run;
+  const std::filesystem::path regression_dir = fuzztest_db_path / "regression";
+  const std::filesystem::path coverage_dir = fuzztest_db_path / "coverage";
 
-    absl::Cleanup clean_up_workdir = [is_workdir_specified, &env] {
-      if (!is_workdir_specified) {
-        CHECK_OK(RemotePathDelete(env.workdir, /*recursively=*/true));
-      }
-    };
-
-    const std::filesystem::path fuzztest_db_path =
-        corpus_database_path / fuzz_tests_to_run[i];
-    const std::filesystem::path regression_dir =
-        fuzztest_db_path / "regression";
-    const std::filesystem::path coverage_dir = fuzztest_db_path / "coverage";
-
-    // Seed the fuzzing session with the latest coverage corpus and regression
-    // inputs from the previous fuzzing session.
-    if (!is_resuming) {
-      CHECK_OK(GenerateSeedCorpusFromConfig(
-          GetSeedCorpusConfig(env, regression_dir.c_str(),
-                              fuzztest_config.replay_coverage_inputs
-                                  ? coverage_dir.c_str()
-                                  : ""),
-          env.binary_name, env.binary_hash))
-          << "while generating the seed corpus";
-    }
-
-    if (!env.fuzztest_single_test_mode) {
-      // TODO: b/338217594 - Call the FuzzTest binary in a flag-agnostic way.
-      constexpr std::string_view kFuzzTestFuzzFlag = "--fuzz=";
-      constexpr std::string_view kFuzzTestReplayCorpusFlag =
-          "--replay_corpus=";
-      std::string_view test_selection_flag = fuzztest_config.only_replay
-                                                 ? kFuzzTestReplayCorpusFlag
-                                                 : kFuzzTestFuzzFlag;
-      env.binary =
-          absl::StrCat(binary, " ", test_selection_flag, fuzz_tests_to_run[i]);
-    }
-
-    absl::Duration time_limit = fuzztest_config.GetTimeLimitPerTest();
-    absl::Duration time_spent = absl::ZeroDuration();
-    const std::string fuzzing_time_file =
-        std::filesystem::path(env.workdir) / "fuzzing_time";
-    if (is_resuming && RemotePathExists(fuzzing_time_file)) {
-      time_spent = ReadFuzzingTime(fuzzing_time_file);
-      time_limit = std::max(time_limit - time_spent, absl::ZeroDuration());
-    }
-    is_resuming = false;
-
-    if (EarlyStopRequested()) {
-      LOG(INFO) << "Skipping test " << fuzz_tests_to_run[i]
-                << " because early stop requested.";
-      continue;
-    }
-
-    LOG(INFO) << (fuzztest_config.only_replay ? "Replaying " : "Fuzzing ")
-              << fuzz_tests_to_run[i] << " for " << time_limit
-              << "\n\tTest binary: " << env.binary;
-
-    const absl::Time start_time = absl::Now();
-    ClearEarlyStopRequestAndSetStopTime(/*stop_time=*/start_time + time_limit);
-    PeriodicAction record_fuzzing_time =
-        RecordFuzzingTime(fuzzing_time_file, start_time - time_spent);
-    Fuzz(env, binary_info, pcs_file_path, callbacks_factory);
-    record_fuzzing_time.Nudge();
-    record_fuzzing_time.Stop();
-
-    if (!stats_root_path.empty()) {
-      const auto stats_dir = stats_root_path / fuzz_tests_to_run[i];
-      CHECK_OK(RemoteMkdir(stats_dir.c_str()));
-      CHECK_OK(RemoteFileRename(
-          workdir.FuzzingStatsPath(),
-          (stats_dir / absl::StrCat("fuzzing_stats_", execution_stamp))
-              .c_str()));
-    }
-
-    // TODO(xinhaoyuan): Have a separate flag to skip corpus updating instead
-    // of checking whether workdir is specified or not.
-    if (fuzztest_config.only_replay || is_workdir_specified) continue;
-
-    // Distill and store the coverage corpus.
-    Distill(env);
-    if (RemotePathExists(coverage_dir.c_str())) {
-      // In the future, we will store k latest coverage corpora for some k, but
-      // for now we only keep the latest one.
-      CHECK_OK(RemotePathDelete(coverage_dir.c_str(), /*recursively=*/true));
-    }
-    CHECK_OK(RemoteMkdir(coverage_dir.c_str()));
-    std::vector<std::string> distilled_corpus_files;
-    CHECK_OK(RemoteGlobMatch(workdir.DistilledCorpusFilePaths().AllShardsGlob(),
-                             distilled_corpus_files));
-    for (const std::string &corpus_file : distilled_corpus_files) {
-      const std::string file_name =
-          std::filesystem::path(corpus_file).filename();
-      CHECK_OK(
-          RemoteFileRename(corpus_file, (coverage_dir / file_name).c_str()));
-    }
-
-    // Deduplicate and update the crashing inputs.
-    const std::filesystem::path crashing_dir = fuzztest_db_path / "crashing";
-    absl::flat_hash_set<std::string> crash_metadata =
-        PruneOldCrashesAndGetRemainingCrashMetadata(crashing_dir, env,
-                                                    callbacks_factory);
-    DeduplicateAndStoreNewCrashes(crashing_dir, workdir, env.total_shards,
-                                  std::move(crash_metadata));
+  // Seed the fuzzing session with the latest coverage corpus and regression
+  // inputs from the previous fuzzing session.
+  if (!is_resuming) {
+    CHECK_OK(GenerateSeedCorpusFromConfig(
+        GetSeedCorpusConfig(
+            env, regression_dir.c_str(),
+            fuzztest_config.replay_coverage_inputs ? coverage_dir.c_str() : ""),
+        env.binary_name, env.binary_hash))
+        << "while generating the seed corpus";
   }
 
+  if (!env.fuzztest_single_test_mode) {
+    // TODO: b/338217594 - Call the FuzzTest binary in a flag-agnostic way.
+    constexpr std::string_view kFuzzTestFuzzFlag = "--fuzz=";
+    constexpr std::string_view kFuzzTestReplayCorpusFlag =
+        "--replay_corpus=";
+    std::string_view test_selection_flag = fuzztest_config.only_replay
+                                               ? kFuzzTestReplayCorpusFlag
+                                               : kFuzzTestFuzzFlag;
+    env.binary =
+        absl::StrCat(binary, " ", test_selection_flag, fuzz_test_to_run);
+  }
+
+  absl::Duration time_limit = fuzztest_config.GetTimeLimitPerTest();
+  absl::Duration time_spent = absl::ZeroDuration();
+  const std::string fuzzing_time_file =
+      std::filesystem::path(env.workdir) / "fuzzing_time";
+  if (is_resuming && RemotePathExists(fuzzing_time_file)) {
+    time_spent = ReadFuzzingTime(fuzzing_time_file);
+    time_limit = std::max(time_limit - time_spent, absl::ZeroDuration());
+  }
+  is_resuming = false;
+
+  if (EarlyStopRequested()) {
+    LOG(INFO) << "Skipping test " << fuzz_test_to_run
+              << " because early stop requested.";
+    return ExitCode();
+  }
+
+  LOG(INFO) << (fuzztest_config.only_replay ? "Replaying " : "Fuzzing ")
+            << fuzz_test_to_run << " for " << time_limit
+            << "\n\tTest binary: " << env.binary;
+
+  start_time = absl::Now();
+  ClearEarlyStopRequestAndSetStopTime(/*stop_time=*/start_time + time_limit);
+  PeriodicAction record_fuzzing_time =
+      RecordFuzzingTime(fuzzing_time_file, start_time - time_spent);
+  Fuzz(env, binary_info, pcs_file_path, callbacks_factory);
+  record_fuzzing_time.Nudge();
+  record_fuzzing_time.Stop();
+
+  if (!stats_root_path.empty()) {
+    const auto stats_dir = stats_root_path / fuzz_test_to_run;
+    CHECK_OK(RemoteMkdir(stats_dir.c_str()));
+    CHECK_OK(RemoteFileRename(
+        workdir.FuzzingStatsPath(),
+        (stats_dir / absl::StrCat("fuzzing_stats_", execution_stamp)).c_str()));
+  }
+
+  // TODO(xinhaoyuan): Have a separate flag to skip corpus updating instead
+  // of checking whether workdir is specified or not.
+  if (fuzztest_config.only_replay || is_workdir_specified) return EXIT_SUCCESS;
+
+  // Distill and store the coverage corpus.
+  Distill(env);
+  if (RemotePathExists(coverage_dir.c_str())) {
+    // In the future, we will store k latest coverage corpora for some k, but
+    // for now we only keep the latest one.
+    CHECK_OK(RemotePathDelete(coverage_dir.c_str(), /*recursively=*/true));
+  }
+  CHECK_OK(RemoteMkdir(coverage_dir.c_str()));
+  std::vector<std::string> distilled_corpus_files;
+  CHECK_OK(RemoteGlobMatch(workdir.DistilledCorpusFilePaths().AllShardsGlob(),
+                           distilled_corpus_files));
+  for (const std::string &corpus_file : distilled_corpus_files) {
+    const std::string file_name = std::filesystem::path(corpus_file).filename();
+    CHECK_OK(RemoteFileRename(corpus_file, (coverage_dir / file_name).c_str()));
+  }
+
+  // Deduplicate and update the crashing inputs.
+  const std::filesystem::path crashing_dir = fuzztest_db_path / "crashing";
+  absl::flat_hash_set<std::string> crash_metadata =
+      PruneOldCrashesAndGetRemainingCrashMetadata(crashing_dir, env,
+                                                  callbacks_factory);
+  DeduplicateAndStoreNewCrashes(crashing_dir, workdir, env.total_shards,
+                                std::move(crash_metadata));
+
   return EXIT_SUCCESS;
 }