aboutsummaryrefslogtreecommitdiff
path: root/pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h
diff options
context:
space:
mode:
Diffstat (limited to 'pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h')
-rw-r--r--pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h399
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