blob: 7c71ec92c43b0403ad842039bc76d218653f391b [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.
#ifndef FUZZTEST_FUZZTEST_INTERNAL_FIXTURE_DRIVER_H_
#define FUZZTEST_FUZZTEST_INTERNAL_FIXTURE_DRIVER_H_
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "./fuzztest/internal/logging.h"
#include "./fuzztest/internal/registration.h"
#include "./fuzztest/internal/type_support.h"
namespace fuzztest::internal {
// The interface for test fixtures with setup and teardown methods that need to
// be explicitly called by the testing framework.
class FixtureWithExplicitSetUp {
public:
virtual ~FixtureWithExplicitSetUp() = default;
virtual void SetUp() = 0;
virtual void TearDown() = 0;
static void SetUpTestSuite() {}
static void TearDownTestSuite() {}
};
// Marker interfaces for specifying the fixture's instantiation semantics:
//
// - Per-iteration semantics: The fixture object is instantiated and discarded
// once per fuzz test iteration.
//
// - Per-fuzz-test semantics: The fixture object is instantiated and discarded
// once per fuzz test. The same object is reused in all fuzz test
// iterations.
class PerIterationFixture : public FixtureWithExplicitSetUp {};
class PerFuzzTestFixture : public FixtureWithExplicitSetUp {};
// The base class for fixture drivers.
//
// A fixture driver is used for maintaining a fixture -- constructing it,
// setting it up, tearing it down, and destructing it -- during a fuzz test. It
// also acts as a proxy to the fixture's target function.
//
// The type parameters are:
// - `RegBase` -- the base class for `Registration` that determines whether
// the fuzz test was registered with domains and seeds.
// - `Fixture` -- the type of the test fixture.
// - `TargetFunction` -- the type of the fixture's target function.
// - Unnamed type used in SFINAE resolution.
template <typename RegBase, typename Fixture, typename TargetFunction,
typename = void>
class FixtureDriver;
// The specialization of the fixture driver base class template.
//
// The type parameters are:
// - `RegBase` -- the base class for `Registration` that determines whether
// the fuzz test was registered with domains and seeds.
// - `Fixture` -- the type of the test fixture.
// - `BaseFixture` -- the class from which `Fixture` is derived and which has
// the target function.
// - `Args...` -- the types of the target function's parameters.
template <typename RegBase, typename Fixture, typename BaseFixture,
typename... Args>
class FixtureDriver<RegBase, Fixture, void (BaseFixture::*)(Args...),
std::enable_if_t<std::is_base_of_v<BaseFixture, Fixture>>> {
public:
using TargetFunction = void (BaseFixture::*)(Args...);
explicit FixtureDriver(
Registration<Fixture, TargetFunction, RegBase> registration)
: registration_(std::move(registration)) {}
// The destructor is pure virtual to make the class abstract.
virtual ~FixtureDriver() = 0;
FixtureDriver(FixtureDriver&&) noexcept;
FixtureDriver& operator=(FixtureDriver&&) = default;
// Methods for setting up and tearing down the fixture. All fixture driver
// implementations must ensure that every sequence of calls of the form
//
// SetUpFuzzTest() SetUpIteration() [ TearDownIteration() SetUpIteration() ]*
//
// results in `fixture_ != nullptr`. Likewise, continuing any such sequence
// with calls to
//
// TearDownIteration() TearDownFuzzTest()
//
// must result in `fixture_ == nullptr`.
virtual void SetUpFuzzTest() {}
virtual void SetUpIteration() {}
virtual void TearDownIteration() {}
virtual void TearDownFuzzTest() {}
void Test(Args... args) {
FUZZTEST_INTERNAL_CHECK_PRECONDITION(
fixture_ != nullptr,
"fixture is nullptr. Did you forget to instantiate it in one of the "
"SetUp methods?");
(fixture_.get()->*registration_.target_function_)(std::move(args)...);
}
decltype(auto) GetSeeds() const {
if constexpr (Registration<Fixture, TargetFunction, RegBase>::kHasSeeds) {
return registration_.seeds_;
} else {
return std::vector<corpus_type_t<decltype(GetDomains())>>{};
}
}
auto GetDomains() const { return registration_.GetDomains(); }
protected:
// The fixture managed by the fixture driver.
std::unique_ptr<Fixture> fixture_;
private:
// The registration that was used to register the fuzz test. Stores
// the pointer to the target function, and the domains and the seeds for the
// target function's parameters.
Registration<Fixture, TargetFunction, RegBase> registration_;
};
template <typename RegBase, typename Fixture, typename BaseFixture,
typename... Args>
FixtureDriver<RegBase, Fixture, void (BaseFixture::*)(Args...),
std::enable_if_t<std::is_base_of_v<BaseFixture, Fixture>>>::
~FixtureDriver() = default;
template <typename RegBase, typename Fixture, typename BaseFixture,
typename... Args>
FixtureDriver<RegBase, Fixture, void (BaseFixture::*)(Args...),
std::enable_if_t<std::is_base_of_v<BaseFixture, Fixture>>>::
FixtureDriver(FixtureDriver&&) noexcept = default;
template <typename RegBase, typename Fixture, typename TargetFunction,
typename = void>
class FixtureDriverImpl;
// 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
// per fuzz test.
template <typename RegBase, typename Fixture, typename BaseFixture,
typename... Args>
class FixtureDriverImpl<
RegBase, Fixture, void (BaseFixture::*)(Args...),
std::enable_if_t<std::conjunction_v<
std::is_default_constructible<Fixture>,
std::is_base_of<BaseFixture, Fixture>,
std::negation<std::is_base_of<FixtureWithExplicitSetUp, Fixture>>>>>
final
: public FixtureDriver<RegBase, Fixture, void (BaseFixture::*)(Args...)> {
public:
using FixtureDriver<RegBase, Fixture,
void (BaseFixture::*)(Args...)>::FixtureDriver;
void SetUpFuzzTest() override {
this->fixture_ = std::make_unique<Fixture>();
}
void TearDownFuzzTest() override { this->fixture_ = nullptr; }
};
// The fixture driver for test fixtures with explicit setup that assume the
// "per-iteration" semantics.
template <typename RegBase, typename Fixture, typename BaseFixture,
typename... Args>
class FixtureDriverImpl<RegBase, Fixture, void (BaseFixture::*)(Args...),
std::enable_if_t<std::conjunction_v<
std::is_default_constructible<Fixture>,
std::is_base_of<BaseFixture, Fixture>,
std::is_base_of<PerIterationFixture, Fixture>>>>
final
: public FixtureDriver<RegBase, Fixture, void (BaseFixture::*)(Args...)> {
public:
using FixtureDriver<RegBase, Fixture,
void (BaseFixture::*)(Args...)>::FixtureDriver;
void SetUpIteration() override {
this->fixture_ = std::make_unique<Fixture>();
this->fixture_->SetUp();
}
void TearDownIteration() override {
this->fixture_->TearDown();
this->fixture_ = nullptr;
}
};
// The fixture driver for test fixtures with explicit setup that assume the
// "per-fuzz-test" semantics.
template <typename RegBase, typename Fixture, typename BaseFixture,
typename... Args>
class FixtureDriverImpl<RegBase, Fixture, void (BaseFixture::*)(Args...),
std::enable_if_t<std::conjunction_v<
std::is_default_constructible<Fixture>,
std::is_base_of<BaseFixture, Fixture>,
std::is_base_of<PerFuzzTestFixture, Fixture>>>>
final
: public FixtureDriver<RegBase, Fixture, void (BaseFixture::*)(Args...)> {
public:
using FixtureDriver<RegBase, Fixture,
void (BaseFixture::*)(Args...)>::FixtureDriver;
void SetUpFuzzTest() override {
this->fixture_ = std::make_unique<Fixture>();
this->fixture_->SetUp();
}
void TearDownFuzzTest() override {
this->fixture_->TearDown();
this->fixture_ = nullptr;
}
};
} // namespace fuzztest::internal
#endif // FUZZTEST_FUZZTEST_INTERNAL_FIXTURE_DRIVER_H_