Internal change PiperOrigin-RevId: 521826652
diff --git a/e2e_tests/functional_test.cc b/e2e_tests/functional_test.cc index 8bdbc7f..bada079 100644 --- a/e2e_tests/functional_test.cc +++ b/e2e_tests/functional_test.cc
@@ -316,6 +316,14 @@ 1, CountSubstrs(std_err, "<<CallCountGoogleTest::TearDownTestSuite()>>")); } +TEST(UnitTestModeTest, DynamicFuzzTestsAreUsed) { + auto [status, std_out, std_err] = + RunWith(GetGTestFilterFlag("MySuiteSeedsFixture.AddCall")); + EXPECT_THAT( + std_err, + HasSubstr("<MySuiteSeedsFixture::GuessedUnguessableExampleSeed>>")); +} + TEST(UnitTestModeTest, GoogleTestWorksWithProtoExtensionsUsedInSeeds) { auto [status, std_out, std_err] = RunWith(GetGTestFilterFlag("MySuite.CheckProtoExtensions"));
diff --git a/e2e_tests/testdata/BUILD b/e2e_tests/testdata/BUILD index 382143e..b49dbf0 100644 --- a/e2e_tests/testdata/BUILD +++ b/e2e_tests/testdata/BUILD
@@ -42,6 +42,7 @@ name = "fuzz_tests_for_functional_testing", testonly = 1, srcs = [ + "fuzz_test_with_dynamic_seeds.cc", "fuzz_tests_for_functional_testing.cc", "fuzz_tests_for_microbenchmarking.cc", "fuzz_tests_using_googletest.cc",
diff --git a/e2e_tests/testdata/fuzz_test_with_dynamic_seeds.cc b/e2e_tests/testdata/fuzz_test_with_dynamic_seeds.cc new file mode 100644 index 0000000..fd03192 --- /dev/null +++ b/e2e_tests/testdata/fuzz_test_with_dynamic_seeds.cc
@@ -0,0 +1,39 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Example fuzz tests that require GoogleTest, for functional testing. +// +// Used by `functional_test` only. We separate these into a different .cc file +// to show that regular FUZZ_TEST work without having to #include GoogleTest. + +#include <cstdio> +#include <limits> + +#include "gtest/gtest.h" +#include "./fuzztest/fuzztest.h" +#include "./fuzztest/googletest_fixture_adapter.h" + +struct MySuiteSeedsFixture { + void AddCall(std::string s) { + if (s == "UnguessableExampleSeed") { + fprintf(stderr, + "<<MySuiteSeedsFixture::GuessedUnguessableExampleSeed>>\n"); + } + } + + std::vector<std::tuple<std::string>> GetDynamicFuzzTestSeeds() { + return {"UnguessableExampleSeed"}; + } +}; +FUZZ_TEST_F(MySuiteSeedsFixture, AddCall);
diff --git a/fuzztest/BUILD b/fuzztest/BUILD index 7bd5dc7..72b487c 100644 --- a/fuzztest/BUILD +++ b/fuzztest/BUILD
@@ -160,6 +160,7 @@ srcs = ["internal/fixture_driver.cc"], hdrs = ["internal/fixture_driver.h"], deps = [ + ":domain", ":logging", ":registration", ":type_support", @@ -171,6 +172,7 @@ size = "small", srcs = ["internal/fixture_driver_test.cc"], deps = [ + ":any", ":domain", ":fixture_driver", ":registration",
diff --git a/fuzztest/internal/fixture_driver.cc b/fuzztest/internal/fixture_driver.cc index e5c5443..c175c88 100644 --- a/fuzztest/internal/fixture_driver.cc +++ b/fuzztest/internal/fixture_driver.cc
@@ -25,9 +25,15 @@ void UntypedFixtureDriver::SetUpIteration() {} void UntypedFixtureDriver::TearDownIteration() {} void UntypedFixtureDriver::TearDownFuzzTest() {} +std::vector<GenericDomainCorpusType> GetDynamicSeeds() { return {}; } + std::vector<GenericDomainCorpusType> UntypedFixtureDriver::GetSeeds() const { return seeds_; } +std::vector<GenericDomainCorpusType> UntypedFixtureDriver::GetDynamicSeeds() { + return {}; +} + std::unique_ptr<UntypedDomainInterface> UntypedFixtureDriver::GetDomains() const { return domain_->Clone();
diff --git a/fuzztest/internal/fixture_driver.h b/fuzztest/internal/fixture_driver.h index fc55add..77e8523 100644 --- a/fuzztest/internal/fixture_driver.h +++ b/fuzztest/internal/fixture_driver.h
@@ -16,10 +16,12 @@ #define FUZZTEST_FUZZTEST_INTERNAL_FIXTURE_DRIVER_H_ #include <memory> +#include <tuple> #include <type_traits> #include <utility> #include <vector> +#include "./fuzztest/internal/domains/domain_base.h" #include "./fuzztest/internal/logging.h" #include "./fuzztest/internal/registration.h" #include "./fuzztest/internal/type_support.h" @@ -78,6 +80,7 @@ virtual void Test(MoveOnlyAny&& args_untyped) const = 0; std::vector<GenericDomainCorpusType> GetSeeds() const; + virtual std::vector<GenericDomainCorpusType> GetDynamicSeeds(); std::unique_ptr<UntypedDomainInterface> GetDomains() const; private: @@ -205,6 +208,16 @@ using FixtureDriver<DomainT, NoFixture, TargetFunction>::FixtureDriver; }; +// HasGetDynamicSeeds<T>::value is true_type if T has a +// GetDynamicSeeds() member. +template <typename T, typename = void> +struct HasGetDynamicFuzzTestSeeds : std::false_type {}; + +template <typename T> +struct HasGetDynamicFuzzTestSeeds< + T, std::void_t<decltype(std::declval<T>().GetDynamicFuzzTestSeeds())>> + : std::true_type {}; + // The fixture driver for default-constructible classes that act like fixtures: // their setup is in the constructor, teardown is in the destructor, and they // have a target function. Such fixtures are instantiated and destructed once @@ -223,6 +236,19 @@ this->fixture_ = std::make_unique<Fixture>(); } void TearDownFuzzTest() override { this->fixture_ = nullptr; } + + std::vector<GenericDomainCorpusType> GetDynamicSeeds() override { + std::vector<GenericDomainCorpusType> seeds; + if constexpr (HasGetDynamicFuzzTestSeeds<Fixture>::value) { + auto typed_seeds = this->fixture_->GetDynamicFuzzTestSeeds(); + seeds.reserve(typed_seeds.size()); + for (auto& seed : typed_seeds) { + seeds.emplace_back(std::in_place_type<std::decay_t<decltype(seed)>>, + std::move(seed)); + } + } + return seeds; + } }; // The fixture driver for test fixtures with explicit setup that assume the
diff --git a/fuzztest/internal/fixture_driver_test.cc b/fuzztest/internal/fixture_driver_test.cc index 45f6f26..2394a09 100644 --- a/fuzztest/internal/fixture_driver_test.cc +++ b/fuzztest/internal/fixture_driver_test.cc
@@ -17,11 +17,13 @@ #include <string> #include <tuple> #include <type_traits> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/types/span.h" #include "./fuzztest/domain.h" +#include "./fuzztest/internal/any.h" #include "./fuzztest/internal/registration.h" #include "./fuzztest/internal/type_support.h" @@ -98,6 +100,37 @@ EXPECT_EQ(CallCountFixture::call_count, 3); } +struct SeedsFixture { + void AddCall(int n) { calls.push_back(n); } + inline static std::vector<int> calls; + inline static std::vector<int> dynamic_seeds; + + std::vector<int> GetDynamicFuzzTestSeeds() { + std::vector<int> ret; + ret.swap(dynamic_seeds); + return ret; + } +}; + +using AddCallFunc = decltype(&SeedsFixture::AddCall); +using AddCallRegBase = DefaultRegistrationBase<CallCountFixture, AddCallFunc>; + +TEST(FixtureDriverTest, DynamicSeeds) { + FixtureDriverImpl<Domain<std::tuple<int>>, SeedsFixture, AddCallFunc> + fixture_driver(&SeedsFixture::AddCall, Arbitrary<std::tuple<int>>(), {}); + + fixture_driver.SetUpFuzzTest(); + std::vector<int> seeds = {177, 182731}; + SeedsFixture::dynamic_seeds = seeds; + + auto output_seeds = fixture_driver.GetDynamicSeeds(); + std::vector<int> int_outputs; + for (const auto& output_seed : output_seeds) { + int_outputs.push_back(output_seed.GetAs<int>()); + } + EXPECT_THAT(int_outputs, UnorderedElementsAre(177, 182731)); +} + struct LifecycleRecordingFixture { LifecycleRecordingFixture() { was_constructed = true; } ~LifecycleRecordingFixture() { was_destructed = true; }
diff --git a/fuzztest/internal/runtime.cc b/fuzztest/internal/runtime.cc index 8d1a049..2bb7785 100644 --- a/fuzztest/internal/runtime.cc +++ b/fuzztest/internal/runtime.cc
@@ -585,6 +585,10 @@ TrySampleAndUpdateInMemoryCorpus(Input{seed}, /*write_to_file=*/false); } + for (const auto& seed : fixture_driver_->GetDynamicSeeds()) { + TrySampleAndUpdateInMemoryCorpus(Input{seed}, + /*write_to_file=*/false); + } } void FuzzTestFuzzerImpl::RunInUnitTestMode() {