diff options
Diffstat (limited to 'pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h')
-rw-r--r-- | pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h b/pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h new file mode 100644 index 000000000..de3b7f382 --- /dev/null +++ b/pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h @@ -0,0 +1,399 @@ +// Copyright 2023 The Pigweed Authors +// +// 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 +// +// https://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. +#pragma once + +/// @file +/// Stubs for the Pigweed-compatible subset of the FuzzTest interface +/// +/// @rst +/// This header provides stubs for the portion of the FuzzTest interface that +/// only depends on permitted C++ standard library `headers`_, including +/// `macros`_ and `domains`_. +/// +/// This header is included when FuzzTest is disabled, e.g. for GN, when +/// ``dir_pw_third_party_fuzztest`` or ``pw_toolchain_FUZZING_ENABLED`` are not +/// set. Otherwise, ``//pw_fuzzer/public/pw_fuzzer/internal/fuzztest.h`` is used +/// instead. +/// +/// .. _domains: +/// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md +/// .. _headers: https://pigweed.dev/docs/style_guide.html#permitted-headers +/// .. _macros: +/// https://github.com/google/fuzztest/blob/main/doc/fuzz-test-macro.md +/// @endrst + +#include <array> +#include <cmath> +#include <initializer_list> +#include <limits> +#include <optional> +#include <string_view> +#include <tuple> +#include <type_traits> +#include <utility> +#include <variant> + +#define FUZZ_TEST(test_suite_name, test_name) \ + TEST(test_suite_name, DISABLED_##test_name) {} \ + auto _pw_fuzzer_##test_suite_name##_##test_name##_FUZZTEST_NOT_PRESENT = \ + fuzztest::internal::TypeCheckFuzzTest(test_name).IgnoreFunction() + +#define FUZZ_TEST_F(test_fixture, test_name) \ + TEST_F(test_fixture, DISABLED_##test_name) {} \ + auto _pw_fuzzer_##test_fixture##_##test_name##_FUZZTEST_NOT_PRESENT = \ + fuzztest::internal::TypeCheckFuzzTest(test_name).IgnoreFunction() + +namespace fuzztest { + +/// Stub for a FuzzTest domain that produces values. +/// +/// In FuzzTest, domains are used to provide values of specific types when +/// fuzzing. However, FuzzTest is only optionally supported on host with Clang. +/// For other build configurations, this struct provides a FuzzTest-compatible +/// stub that can be used to perform limited type-checking at build time. +/// +/// Fuzzer authors must not invoke this type directly. Instead, use the factory +/// methods for domains such as `Arbitrary`, `VectorOf`, `Map`, etc. +template <typename T> +struct Domain { + using value_type = T; +}; + +namespace internal { + +/// Stub for a FuzzTest domain that produces containers of values. +/// +/// This struct is an extension of `Domain` that add stubs for the methods that +/// control container size. +template <typename T> +struct ContainerDomain : public Domain<T> { + template <typename U, typename = std::enable_if_t<std::is_integral_v<U>>> + ContainerDomain<T>& WithSize(U) { + return *this; + } + + template <typename U, typename = std::enable_if_t<std::is_integral_v<U>>> + ContainerDomain<T>& WithMinSize(U) { + return *this; + } + + template <typename U, typename = std::enable_if_t<std::is_integral_v<U>>> + ContainerDomain<T>& WithMaxSize(U) { + return *this; + } +}; + +/// Stub for a FuzzTest domain that produces optional values. +/// +/// This struct is an extension of `Domain` that add stubs for the methods that +/// control nullability. +template <typename T> +struct OptionalDomain : public Domain<T> { + OptionalDomain<T>& SetAlwaysNull() { return *this; } + OptionalDomain<T>& SetWithoutNull() { return *this; } +}; + +/// Register a FuzzTest stub. +/// +/// FuzzTest is only optionally supported on host with Clang. For other build +/// configurations, this struct provides a FuzzTest-compatible stub of a test +/// registration that only performs limited type-checking at build time. +/// +/// Fuzzer authors must not invoke this type directly. Instead, use the +/// `FUZZ_TEST` and/or `FUZZ_TEST_F` macros. +template <typename TargetFunction> +struct TypeCheckFuzzTest { + TypeCheckFuzzTest(TargetFunction) {} + + TypeCheckFuzzTest<TargetFunction>& IgnoreFunction() { return *this; } + + template <int&... ExplicitArgumentBarrier, + typename... Domains, + typename T = std::decay_t< + std::invoke_result_t<TargetFunction, + const typename Domains::value_type&...>>> + TypeCheckFuzzTest<TargetFunction>& WithDomains(Domains...) { + return *this; + } + + template <int&... ExplicitArgumentBarrier, + typename... Seeds, + typename T = std::decay_t< + std::invoke_result_t<TargetFunction, const Seeds&...>>> + TypeCheckFuzzTest<TargetFunction>& WithSeeds(Seeds...) { + return *this; + } +}; + +} // namespace internal + +// The remaining functions match those defined by fuzztest/fuzztest.h. +// +// This namespace is here only as a way to disable ADL (argument-dependent +// lookup). Names should be used from the fuzztest:: namespace. +namespace internal_no_adl { + +//////////////////////////////////////////////////////////////// +// Arbitrary domains +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#arbitrary-domains + +template <typename T> +auto Arbitrary() { + return Domain<T>{}; +} + +//////////////////////////////////////////////////////////////// +// Other miscellaneous domains +// These typically appear later in docs and tests. They are placed early in this +// file to allow other domains to be defined using them. + +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#oneof +template <int&... ExplicitArgumentBarrier, typename T, typename... Domains> +auto OneOf(Domain<T>, Domains...) { + return Domain<T>{}; +} + +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#oneof +template <typename T> +auto Just(T) { + return Domain<T>{}; +} + +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#map +template <int&... ExplicitArgumentBarrier, typename Mapper, typename... Inner> +auto Map(Mapper, Inner...) { + return Domain<std::decay_t< + std::invoke_result_t<Mapper, typename Inner::value_type&...>>>{}; +} + +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#flatmap +template <typename FlatMapper, typename... Inner> +using FlatMapOutputDomain = std::decay_t< + std::invoke_result_t<FlatMapper, typename Inner::value_type&...>>; +template <int&... ExplicitArgumentBarrier, + typename FlatMapper, + typename... Inner> +auto FlatMap(FlatMapper, Inner...) { + return Domain< + typename FlatMapOutputDomain<FlatMapper, Inner...>::value_type>{}; +} + +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#filter +template <int&... ExplicitArgumentBarrier, typename T, typename Pred> +auto Filter(Pred, Domain<T>) { + return Domain<T>{}; +} + +//////////////////////////////////////////////////////////////// +// Numerical domains +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains + +template <typename T> +auto InRange(T min, T max) { + return Filter([min, max](T t) { return min <= t && t <= max; }, + Arbitrary<T>()); +} + +template <typename T> +auto NonZero() { + return Filter([](T t) { return t != 0; }, Arbitrary<T>()); +} + +template <typename T> +auto Positive() { + if constexpr (std::is_floating_point_v<T>) { + return InRange<T>(std::numeric_limits<T>::denorm_min(), + std::numeric_limits<T>::max()); + } else { + return InRange<T>(T{1}, std::numeric_limits<T>::max()); + } +} + +template <typename T> +auto NonNegative() { + return InRange<T>(T{}, std::numeric_limits<T>::max()); +} + +template <typename T> +auto Negative() { + static_assert(!std::is_unsigned_v<T>, + "Negative<T>() can only be used with with signed T-s! " + "For char, consider using signed char."); + if constexpr (std::is_floating_point_v<T>) { + return InRange<T>(std::numeric_limits<T>::lowest(), + -std::numeric_limits<T>::denorm_min()); + } else { + return InRange<T>(std::numeric_limits<T>::min(), T{-1}); + } +} + +template <typename T> +auto NonPositive() { + static_assert(!std::is_unsigned_v<T>, + "NonPositive<T>() can only be used with with signed T-s! " + "For char, consider using signed char."); + return InRange<T>(std::numeric_limits<T>::lowest(), T{}); +} + +template <typename T> +auto Finite() { + static_assert(std::is_floating_point_v<T>, + "Finite<T>() can only be used with floating point types!"); + return Filter([](T f) { return std::isfinite(f); }, Arbitrary<T>()); +} + +//////////////////////////////////////////////////////////////// +// Character domains +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains + +inline auto NonZeroChar() { return Positive<char>(); } +inline auto NumericChar() { return InRange<char>('0', '9'); } +inline auto LowerChar() { return InRange<char>('a', 'z'); } +inline auto UpperChar() { return InRange<char>('A', 'Z'); } +inline auto AlphaChar() { return OneOf(LowerChar(), UpperChar()); } +inline auto AlphaNumericChar() { return OneOf(AlphaChar(), NumericChar()); } +inline auto AsciiChar() { return InRange<char>(0, 127); } +inline auto PrintableAsciiChar() { return InRange<char>(32, 126); } + +//////////////////////////////////////////////////////////////// +// Regular expression domains + +// TODO: b/285775246 - Add support for `fuzztest::InRegexp`. +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#inregexp-domains +// inline auto InRegexp(std::string_view) { +// return Domain<std::string_view>{}; +// } + +//////////////////////////////////////////////////////////////// +// Enumerated domains +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#elementof-domains + +template <typename T> +auto ElementOf(std::initializer_list<T>) { + return Domain<T>{}; +} + +template <typename T> +auto BitFlagCombinationOf(std::initializer_list<T>) { + return Domain<T>{}; +} + +//////////////////////////////////////////////////////////////// +// Container domains +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators + +template <typename T, int&... ExplicitArgumentBarrier, typename U> +auto ContainerOf(Domain<U>) { + return internal::ContainerDomain<T>{}; +} + +template <template <typename, typename...> class T, + int&... ExplicitArgumentBarrier, + typename U> +auto ContainerOf(Domain<U>) { + return internal::ContainerDomain<T<U>>{}; +} + +template <typename T, int&... ExplicitArgumentBarrier, typename U> +auto UniqueElementsContainerOf(Domain<U>) { + return internal::ContainerDomain<T>{}; +} + +template <int&... ExplicitArgumentBarrier, typename T> +auto NonEmpty(internal::ContainerDomain<T> inner) { + return inner.WithMinSize(1); +} + +//////////////////////////////////////////////////////////////// +// Aggregate domains +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators + +template <int&... ExplicitArgumentBarrier, typename T, typename... Domains> +auto ArrayOf(Domain<T>, Domains... others) { + return Domain<std::array<T, 1 + sizeof...(others)>>{}; +} + +template <int N, int&... ExplicitArgumentBarrier, typename T> +auto ArrayOf(const Domain<T>&) { + return Domain<std::array<T, N>>{}; +} + +template <typename T, int&... ExplicitArgumentBarrier, typename... Inner> +auto StructOf(Inner...) { + return Domain<T>{}; +} + +template <typename T, int&... ExplicitArgumentBarrier> +auto ConstructorOf() { + return Domain<T>{}; +} + +template <typename T, + int&... ExplicitArgumentBarrier, + typename U, + typename... Inner> +auto ConstructorOf(Domain<U>, Inner... inner) { + return ConstructorOf<T>(inner...); +} + +template <int&... ExplicitArgumentBarrier, typename T1, typename T2> +auto PairOf(Domain<T1>, Domain<T2>) { + return Domain<std::pair<T1, T2>>{}; +} + +template <int&... ExplicitArgumentBarrier, typename... Inner> +auto TupleOf(Inner...) { + return Domain<std::tuple<typename Inner::value_type...>>{}; +} + +template <typename T, int&... ExplicitArgumentBarrier, typename... Inner> +auto VariantOf(Inner...) { + return Domain<T>{}; +} + +template <int&... ExplicitArgumentBarrier, typename... Inner> +auto VariantOf(Inner...) { + return Domain<std::variant<typename Inner::value_type...>>{}; +} + +template <template <typename> class Optional, + int&... ExplicitArgumentBarrier, + typename T> +auto OptionalOf(Domain<T>) { + return internal::OptionalDomain<Optional<T>>{}; +} + +template <int&... ExplicitArgumentBarrier, typename T> +auto OptionalOf(Domain<T> inner) { + return OptionalOf<std::optional>(inner); +} + +template <typename T> +auto NullOpt() { + return internal::OptionalDomain<std::optional<T>>{}.SetAlwaysNull(); +} + +template <int&... ExplicitArgumentBarrier, typename T> +auto NonNull(internal::OptionalDomain<T> inner) { + return inner.SetWithoutNull(); +} + +} // namespace internal_no_adl + +// Inject the names from internal_no_adl into fuzztest, without allowing for +// ADL. Note that an `inline` namespace would not have this effect (ie it would +// still allow ADL to trigger). +using namespace internal_no_adl; // NOLINT + +} // namespace fuzztest |