Support FUZZTEST_MAX_JOBS environment variable to cap parallel jobs. PiperOrigin-RevId: 955855667
diff --git a/e2e_tests/functional_test.cc b/e2e_tests/functional_test.cc index 6133473..af8f9e2 100644 --- a/e2e_tests/functional_test.cc +++ b/e2e_tests/functional_test.cc
@@ -772,6 +772,12 @@ EXPECT_THAT(status, Eq(ExitCode(0))); } +TEST_F(GenericCommandLineInterfaceTest, IgnoresInvalidJobsLimitInEnvVar) { + auto [status, std_out, std_err] = RunWith( + /*flags=*/{{"jobs", "10"}}, /*env=*/{{"FUZZTEST_MAX_JOBS", "-1"}}); + EXPECT_THAT_LOG(std_err, HasSubstr("will not limit jobs")); +} + // Tests for the FuzzTest command line interface in fuzzing mode, which can only // run with coverage instrumentation enabled. class FuzzingModeCommandLineInterfaceTest @@ -847,6 +853,16 @@ #endif // FUZZTEST_USE_CENTIPEDE } +TEST_F(FuzzingModeCommandLineInterfaceTest, LimitsJobsWhenEnvVarIsSet) { + auto [status, std_out, std_err] = RunWith( + {{"fuzz", "MySuite.PassesWithPositiveInput"}, {"jobs", "10"}}, + {{"FUZZTEST_MAX_JOBS", "2"}, {"FUZZTEST_MAX_FUZZING_RUNS", "10"}}); +#ifdef FUZZTEST_USE_CENTIPEDE + EXPECT_THAT_LOG(std_err, HasSubstr("Shard: 0/2")); +#endif // FUZZTEST_USE_CENTIPEDE + EXPECT_THAT(status, Eq(ExitCode(0))); +} + TEST_F(FuzzingModeCommandLineInterfaceTest, LimitsFuzzingRunsWhenTimeoutIsSet) { auto [status, std_out, std_err] = RunWith( {{"fuzz", "MySuite.PassesWithPositiveInput"}, {"fuzz_for", "1s"}});
diff --git a/fuzztest/init_fuzztest.cc b/fuzztest/init_fuzztest.cc index 0653dce..c2fdc44 100644 --- a/fuzztest/init_fuzztest.cc +++ b/fuzztest/init_fuzztest.cc
@@ -22,6 +22,7 @@ #include "absl/flags/parse.h" #include "absl/flags/reflection.h" #include "absl/strings/match.h" +#include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" @@ -352,6 +353,19 @@ FUZZTEST_CHECK(!jobs.has_value() || *jobs > 0) << "If specified, --" << FUZZTEST_FLAG(jobs).Name() << " must be positive."; + size_t num_jobs = jobs.value_or(0); + if (const char* max_jobs_env = std::getenv("FUZZTEST_MAX_JOBS")) { + if (size_t max_jobs; + absl::SimpleAtoi(max_jobs_env, &max_jobs) && max_jobs > 0) { + if (num_jobs > max_jobs) { + num_jobs = max_jobs; + } + } else { + absl::FPrintF(stderr, + "[!] Failed to parse FUZZTEST_MAX_JOBS as a positive " + "integer - will not limit jobs.\n"); + } + } std::string corpus_database = absl::GetFlag(FUZZTEST_FLAG(corpus_database)); if (!corpus_database.empty() && corpus_database[0] != '/' && std::getenv("TEST_SRCDIR")) { @@ -380,7 +394,7 @@ absl::GetFlag(FUZZTEST_FLAG(time_limit_per_input)), time_limit, time_budget_type, - jobs.value_or(0), + num_jobs, absl::GetFlag(FUZZTEST_FLAG(internal_centipede_command)), absl::GetFlag(FUZZTEST_FLAG(internal_crashing_input_to_reproduce)), };