blob: f36e3b896d3626e76d8231656e804624e0c7d3f3 [file] [log] [blame]
// 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.
#include "./fuzztest/internal/fixture_driver.h"
#include <string>
#include <tuple>
#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/logging.h"
#include "./fuzztest/internal/registration.h"
#include "./fuzztest/internal/type_support.h"
namespace fuzztest::internal {
namespace {
using ::testing::UnorderedElementsAre;
struct CallCountFixture {
void IncrementCallCount(int n) { call_count += n; }
inline static int call_count;
};
using IncrementCallCountFunc = decltype(&CallCountFixture::IncrementCallCount);
using CallCountRegBase =
DefaultRegistrationBase<CallCountFixture, IncrementCallCountFunc>;
template <typename... T>
MoveOnlyAny MakeArgs(T... t) {
return MoveOnlyAny(std::in_place_type<std::tuple<T...>>, std::tuple(t...));
}
TEST(FixtureDriverTest, PropagatesCallToTargetFunction) {
FixtureDriverImpl<Domain<std::tuple<int>>, CallCountFixture,
IncrementCallCountFunc, void*>
fixture_driver(&CallCountFixture::IncrementCallCount,
Arbitrary<std::tuple<int>>(), {}, nullptr);
CallCountFixture::call_count = 0;
fixture_driver.SetUpFuzzTest();
fixture_driver.SetUpIteration();
fixture_driver.Test(MakeArgs(7));
EXPECT_EQ(CallCountFixture::call_count, 7);
}
TEST(FixtureDriverTest, ReusesSameFixtureObjectDuringFuzzTest) {
FixtureDriverImpl<Domain<std::tuple<int>>, CallCountFixture,
IncrementCallCountFunc, void*>
fixture_driver(&CallCountFixture::IncrementCallCount,
Arbitrary<std::tuple<int>>(), {}, nullptr);
CallCountFixture::call_count = 0;
fixture_driver.SetUpFuzzTest();
fixture_driver.SetUpIteration();
fixture_driver.Test(MakeArgs(3));
fixture_driver.TearDownIteration();
fixture_driver.SetUpIteration();
fixture_driver.Test(MakeArgs(3));
fixture_driver.TearDownIteration();
fixture_driver.SetUpIteration();
fixture_driver.Test(MakeArgs(4));
EXPECT_EQ(CallCountFixture::call_count, 10);
}
struct DerivedCallCountFixture : CallCountFixture {};
TEST(FixtureDriverTest, PropagatesCallToTargetFunctionOnBaseFixture) {
FixtureDriverImpl<Domain<std::tuple<int>>, DerivedCallCountFixture,
IncrementCallCountFunc, void*>
fixture_driver(&DerivedCallCountFixture::IncrementCallCount,
Arbitrary<std::tuple<int>>(), {}, nullptr);
CallCountFixture::call_count = 0;
fixture_driver.SetUpFuzzTest();
fixture_driver.SetUpIteration();
fixture_driver.Test(MakeArgs(3));
EXPECT_EQ(CallCountFixture::call_count, 3);
}
struct LifecycleRecordingFixture {
LifecycleRecordingFixture() { was_constructed = true; }
~LifecycleRecordingFixture() { was_destructed = true; }
void NoOp() {}
static void Reset() {
was_constructed = false;
was_destructed = false;
}
static bool was_constructed;
static bool was_destructed;
};
bool LifecycleRecordingFixture::was_constructed = false;
bool LifecycleRecordingFixture::was_destructed = false;
TEST(FixtureDriverTest, FixtureGoesThroughCompleteLifecycle) {
using NoOpFunc = decltype(&LifecycleRecordingFixture::NoOp);
FixtureDriverImpl<Domain<std::tuple<>>, LifecycleRecordingFixture, NoOpFunc,
void*>
fixture_driver(&LifecycleRecordingFixture::NoOp,
Arbitrary<std::tuple<>>(), {}, nullptr);
LifecycleRecordingFixture::Reset();
ASSERT_TRUE(!LifecycleRecordingFixture::was_constructed &&
!LifecycleRecordingFixture::was_destructed);
fixture_driver.SetUpFuzzTest();
fixture_driver.SetUpIteration();
EXPECT_TRUE(LifecycleRecordingFixture::was_constructed);
fixture_driver.TearDownIteration();
EXPECT_TRUE(!LifecycleRecordingFixture::was_destructed);
fixture_driver.TearDownFuzzTest();
EXPECT_TRUE(LifecycleRecordingFixture::was_destructed);
}
template <typename InstantiationType>
struct LifecycleRecordingFixtureWithExplicitSetUp : LifecycleRecordingFixture,
InstantiationType {
~LifecycleRecordingFixtureWithExplicitSetUp() override {
LifecycleRecordingFixture::~LifecycleRecordingFixture();
}
void SetUp() override { was_set_up = true; }
void TearDown() override { was_torn_down = true; }
static void Reset() {
LifecycleRecordingFixture::Reset();
was_set_up = false;
was_torn_down = false;
}
static bool was_set_up;
static bool was_torn_down;
};
template <typename InstantiationType>
bool LifecycleRecordingFixtureWithExplicitSetUp<InstantiationType>::was_set_up =
false;
template <typename InstantiationType>
bool LifecycleRecordingFixtureWithExplicitSetUp<
InstantiationType>::was_torn_down = false;
TEST(FixtureDriverTest, PerIterationFixtureGoesThroughCompleteLifecycle) {
using LifecycleRecordingPerIterationFixture =
LifecycleRecordingFixtureWithExplicitSetUp<PerIterationFixture>;
using NoOpFunc = decltype(&LifecycleRecordingPerIterationFixture::NoOp);
FixtureDriverImpl<Domain<std::tuple<>>, LifecycleRecordingPerIterationFixture,
NoOpFunc, void*>
fixture_driver(&LifecycleRecordingPerIterationFixture::NoOp,
Arbitrary<std::tuple<>>(), {}, nullptr);
LifecycleRecordingPerIterationFixture::Reset();
ASSERT_TRUE(!LifecycleRecordingPerIterationFixture::was_constructed &&
!LifecycleRecordingPerIterationFixture::was_set_up &&
!LifecycleRecordingPerIterationFixture::was_torn_down &&
!LifecycleRecordingPerIterationFixture::was_destructed);
fixture_driver.SetUpFuzzTest();
fixture_driver.SetUpIteration();
EXPECT_TRUE(LifecycleRecordingPerIterationFixture::was_constructed &&
LifecycleRecordingPerIterationFixture::was_set_up &&
!LifecycleRecordingPerIterationFixture::was_torn_down &&
!LifecycleRecordingPerIterationFixture::was_destructed);
fixture_driver.TearDownIteration();
EXPECT_TRUE(LifecycleRecordingPerIterationFixture::was_torn_down &&
LifecycleRecordingPerIterationFixture::was_destructed);
}
TEST(FixtureDriverTest, PerFuzzTestFixtureGoesThroughCompleteLifecycle) {
using LifecycleRecordingPerFuzzTestFixture =
LifecycleRecordingFixtureWithExplicitSetUp<PerFuzzTestFixture>;
using NoOpFunc = decltype(&LifecycleRecordingPerFuzzTestFixture::NoOp);
FixtureDriverImpl<Domain<std::tuple<>>, LifecycleRecordingPerFuzzTestFixture,
NoOpFunc, void*>
fixture_driver(&LifecycleRecordingPerFuzzTestFixture::NoOp,
Arbitrary<std::tuple<>>(), {}, nullptr);
LifecycleRecordingPerFuzzTestFixture::Reset();
ASSERT_TRUE(!LifecycleRecordingPerFuzzTestFixture::was_constructed &&
!LifecycleRecordingPerFuzzTestFixture::was_set_up &&
!LifecycleRecordingPerFuzzTestFixture::was_torn_down &&
!LifecycleRecordingPerFuzzTestFixture::was_destructed);
fixture_driver.SetUpFuzzTest();
fixture_driver.SetUpIteration();
EXPECT_TRUE(LifecycleRecordingPerFuzzTestFixture::was_constructed &&
LifecycleRecordingPerFuzzTestFixture::was_set_up);
fixture_driver.TearDownIteration();
EXPECT_TRUE(!LifecycleRecordingPerFuzzTestFixture::was_torn_down &&
!LifecycleRecordingPerFuzzTestFixture::was_destructed);
fixture_driver.TearDownFuzzTest();
EXPECT_TRUE(LifecycleRecordingPerFuzzTestFixture::was_torn_down &&
LifecycleRecordingPerFuzzTestFixture::was_destructed);
}
template <typename T>
std::vector<T> UnpackGenericValues(
absl::Span<const CopyableAny> generic_values) {
std::vector<T> values;
values.reserve(generic_values.size());
for (const auto& generic_value : generic_values) {
FUZZTEST_INTERNAL_CHECK(generic_value.Has<T>(),
"Generic value of a wrong type.");
values.push_back(generic_value.GetAs<T>());
}
return values;
}
void TakesInt(int) {}
std::vector<std::tuple<int>> GetSeeds() { return {{7}, {42}}; }
TEST(FixtureDriverTest, PropagatesSeedsFromFreeSeedProvider) {
FixtureDriverImpl<Domain<std::tuple<int>>, NoFixture, decltype(&TakesInt),
decltype(&GetSeeds)>
fixture_driver(&TakesInt, Arbitrary<std::tuple<int>>(), {}, &GetSeeds);
EXPECT_THAT(UnpackGenericValues<std::tuple<int>>(fixture_driver.GetSeeds()),
UnorderedElementsAre(std::tuple{7}, std::tuple{42}));
}
struct FixtureWithSeedProvider {
void TakesInt(int) {}
std::vector<std::tuple<int>> GetSeeds() { return {{7}, {42}}; }
};
TEST(FixtureDriverTest, PropagatesSeedsFromSeedProviderOnFixture) {
FixtureDriverImpl<Domain<std::tuple<int>>, FixtureWithSeedProvider,
decltype(&FixtureWithSeedProvider::TakesInt),
decltype(&FixtureWithSeedProvider::GetSeeds)>
fixture_driver(&FixtureWithSeedProvider::TakesInt,
Arbitrary<std::tuple<int>>(), {},
&FixtureWithSeedProvider::GetSeeds);
fixture_driver.SetUpFuzzTest();
EXPECT_THAT(UnpackGenericValues<std::tuple<int>>(fixture_driver.GetSeeds()),
UnorderedElementsAre(std::tuple{7}, std::tuple{42}));
}
struct DerivedFixtureWithSeedProvider : FixtureWithSeedProvider {};
TEST(FixtureDriverTest, PropagatesSeedsFromSeedProviderOnBaseFixture) {
FixtureDriverImpl<Domain<std::tuple<int>>, DerivedFixtureWithSeedProvider,
decltype(&DerivedFixtureWithSeedProvider::TakesInt),
decltype(&DerivedFixtureWithSeedProvider::GetSeeds)>
fixture_driver(&DerivedFixtureWithSeedProvider::TakesInt,
Arbitrary<std::tuple<int>>(), {},
&DerivedFixtureWithSeedProvider::GetSeeds);
fixture_driver.SetUpFuzzTest();
EXPECT_THAT(UnpackGenericValues<std::tuple<int>>(fixture_driver.GetSeeds()),
UnorderedElementsAre(std::tuple{7}, std::tuple{42}));
}
TEST(FixtureDriverTest, DiesIfConversionOfSeedsFromSeedProviderFails) {
FixtureDriverImpl<Domain<std::tuple<int>>, NoFixture, decltype(&TakesInt),
decltype(&GetSeeds)>
fixture_driver(
&TakesInt,
Filter([](std::tuple<int> i) { return std::get<0>(i) % 2 == 0; },
Arbitrary<std::tuple<int>>()),
{}, &GetSeeds);
EXPECT_DEATH_IF_SUPPORTED(fixture_driver.GetSeeds(), "Invalid seed value");
}
} // namespace
} // namespace fuzztest::internal