| // 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 fuzztest.h |
| /// Pigweed interface to FuzzTest |
| /// |
| /// @rst |
| /// This header exposes the portion of the FuzzTest interface that only depends |
| /// on permitted C++ standard library `headers`_, including `macros`_ and |
| /// `domains`_. |
| /// |
| /// It also extends the interface to provide domains for common Pigweed types, |
| /// such as those from the following modules: |
| /// |
| /// * :ref:`module-pw_containers` |
| /// * :ref:`module-pw_result` |
| /// * :ref:`module-pw_status` |
| /// * :ref:`module-pw_string` |
| /// |
| /// .. _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 "pw_containers/flat_map.h" |
| #include "pw_containers/inline_deque.h" |
| #include "pw_containers/inline_queue.h" |
| #include "pw_containers/intrusive_list.h" |
| #include "pw_containers/vector.h" |
| #include "pw_fuzzer/internal/fuzztest.h" |
| #include "pw_result/result.h" |
| #include "pw_status/status.h" |
| #include "pw_status/status_with_size.h" |
| #include "pw_string/string.h" |
| |
| namespace pw::fuzzer { |
| |
| template <typename T> |
| using Domain = fuzztest::Domain<T>; |
| |
| //////////////////////////////////////////////////////////////// |
| // Arbitrary domains |
| // Additional specializations are provided with the Pigweed domains. |
| |
| /// @struct ArbitraryImpl |
| /// @fn Arbitrary |
| /// |
| /// Produces values for fuzz target function parameters. |
| /// |
| /// This defines a new template rather than using the `fuzztest` one directly in |
| /// order to facilitate specializations for Pigweed types. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#arbitrary-domains |
| template <typename T, typename = void> |
| struct ArbitraryImpl { |
| auto operator()() { return fuzztest::Arbitrary<T>(); } |
| }; |
| template <typename T> |
| auto Arbitrary() { |
| return ArbitraryImpl<T>()(); |
| } |
| |
| //////////////////////////////////////////////////////////////// |
| // Numerical domains |
| |
| /// Produces values from a closed interval. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains |
| using fuzztest::InRange; |
| |
| /// Like Arbitrary<T>(), but does not produce the zero value. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains |
| using fuzztest::NonZero; |
| |
| /// Produces numbers greater than zero. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains |
| using fuzztest::Positive; |
| |
| /// Produces the zero value and numbers greater than zero. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains |
| using fuzztest::NonNegative; |
| |
| /// Produces numbers less than zero. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains |
| using fuzztest::Negative; |
| |
| /// Produces the zero value and numbers less than zero. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains |
| using fuzztest::NonPositive; |
| |
| /// Produces floating points numbers that are neither infinity nor NaN. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains |
| using fuzztest::Finite; |
| |
| //////////////////////////////////////////////////////////////// |
| // Character domains |
| |
| /// Produces any char except '\0'. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains |
| using fuzztest::NonZeroChar; |
| |
| /// Alias for `InRange('0', '9')`. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains |
| using fuzztest::NumericChar; |
| |
| /// Alias for `InRange('a', 'z')`. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains |
| using fuzztest::LowerChar; |
| |
| /// Alias for `InRange('A', 'Z')`. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains |
| using fuzztest::UpperChar; |
| |
| /// Alias for `OneOf(LowerChar(), UpperChar())`. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains |
| using fuzztest::AlphaChar; |
| |
| /// Alias for `OneOf(AlphaChar(), NumericChar())`. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains |
| using fuzztest::AlphaNumericChar; |
| |
| /// Produces printable characters (`InRange<char>(32, 126)`). |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains |
| using fuzztest::PrintableAsciiChar; |
| |
| /// Produces ASCII characters (`InRange<char>(0, 127)`). |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains |
| using fuzztest::AsciiChar; |
| |
| //////////////////////////////////////////////////////////////// |
| // Regular expression domains |
| |
| // TODO: b/285775246 - Add support for `fuzztest::InRegexp`. |
| // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#inregexp-domains |
| // using fuzztest::InRegexp; |
| |
| //////////////////////////////////////////////////////////////// |
| // Enumerated domains |
| |
| /// Produces values from an enumerated set. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#elementof-domains |
| using fuzztest::ElementOf; |
| |
| /// Produces combinations of binary flags from a provided list. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#bitflagcombinationof-domains |
| using fuzztest::BitFlagCombinationOf; |
| |
| //////////////////////////////////////////////////////////////// |
| // Container domains |
| |
| /// @struct ContainerOfImpl |
| /// @fn ContainerOf |
| /// |
| /// Produces containers of elements provided by inner domains. |
| /// |
| /// This defines a new template rather than using the `fuzztest` one directly in |
| /// order to specify the static container capacity as part of the container |
| /// type. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators |
| template <typename Container, typename = void> |
| struct ContainerOfImpl { |
| template <int&... ExplicitArgumentBarrier, typename Inner> |
| auto operator()(Inner inner) { |
| return fuzztest::ContainerOf<Container>(std::move(inner)) |
| .WithMaxSize(Container{}.max_size()); |
| } |
| }; |
| template <typename Container, int&... ExplicitArgumentBarrier, typename Inner> |
| auto ContainerOf(Inner inner) { |
| return ContainerOfImpl<Container>()(std::move(inner)); |
| } |
| |
| namespace internal { |
| |
| template <typename T, typename = void> |
| struct IsContainer : std::false_type {}; |
| |
| template <typename T> |
| struct IsContainer<T, std::void_t<decltype(T().begin(), T().end())>> |
| : std::true_type {}; |
| |
| } // namespace internal |
| |
| /// Produces containers of at least one element provided by inner domains. |
| /// |
| /// The container type is given by a template parameter. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators |
| using fuzztest::NonEmpty; |
| |
| /// @struct UniqueElementsContainerOfImpl |
| /// @fn UniqueElementsContainerOf |
| /// |
| /// Produces containers of unique elements provided by inner domains. |
| /// |
| /// This defines a new template rather than using the `fuzztest` one directly in |
| /// order to specify the static container capacity as part of the container |
| /// type. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators |
| template <typename Container, typename = void> |
| struct UniqueElementsContainerOfImpl { |
| template <int&... ExplicitArgumentBarrier, typename Inner> |
| auto operator()(Inner inner) { |
| return fuzztest::UniqueElementsContainerOf<Container>(std::move(inner)) |
| .WithMaxSize(Container{}.max_size()); |
| } |
| }; |
| template <typename Container, int&... ExplicitArgumentBarrier, typename Inner> |
| auto UniqueElementsContainerOf(Inner inner) { |
| return UniqueElementsContainerOfImpl<Container>()(std::move(inner)); |
| } |
| |
| //////////////////////////////////////////////////////////////// |
| // Aggregate domains |
| |
| /// Produces std::array<T>s of elements provided by inner domains. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators |
| using fuzztest::ArrayOf; |
| |
| /// Specializes @cpp_class{pw::fuzzer::ContainerOfImpl} for arrays, which do not |
| /// need a maximum size applied. |
| /// |
| /// @param[in] inner Domain the produces values of type `T`. |
| /// |
| /// @retval Domain that produces `std::array<T, kArraySize>`s. |
| template <typename T, size_t N> |
| struct ContainerOfImpl<std::array<T, N>> { |
| template <int&... ExplicitArgumentBarrier, typename Inner> |
| auto operator()(Inner inner) { |
| return fuzztest::ContainerOf<std::array<T, N>>(std::move(inner)); |
| } |
| }; |
| |
| /// Produces user-defined structs. |
| /// |
| /// The struct type is given by a template parameter. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#structof |
| using fuzztest::StructOf; |
| |
| /// Produces user-defined objects. |
| /// |
| /// The class type is given by a template parameter. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#constructorof |
| using fuzztest::ConstructorOf; |
| |
| /// Produces `std::pair<T1, T2>`s from provided inner domains. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#pairof |
| using fuzztest::PairOf; |
| |
| /// Produces `std::tuple<T...>`s from provided inner domains. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#tupleof |
| using fuzztest::TupleOf; |
| |
| /// Produces `std::variant<T...>` from provided inner domains. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#variantof |
| using fuzztest::VariantOf; |
| |
| /// Produces `std::optional<T>` from provided inner domain. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#optionalof |
| using fuzztest::OptionalOf; |
| |
| /// Produces `std::optional<T>` from provided inner domain that is null. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#optionalof |
| using fuzztest::NullOpt; |
| |
| /// Produces `std::optional<T>` from provided inner domain that is not null. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#optionalof |
| using fuzztest::NonNull; |
| |
| //////////////////////////////////////////////////////////////// |
| // Other miscellaneous domains |
| |
| /// Produces values by choosing between provided inner domains. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#oneof |
| using fuzztest::OneOf; |
| |
| /// Produces values equal to the provided value. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#oneof |
| using fuzztest::Just; |
| |
| /// Produces values by applying a function to values from provided domains. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#map |
| using fuzztest::Map; |
| |
| /// Creates a domain by applying a function to values from provided domains. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#flatmap |
| using fuzztest::FlatMap; |
| |
| /// Produces values from a provided domain that will cause a provided predicate |
| /// to return true. |
| /// |
| /// See |
| /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#filter |
| using fuzztest::Filter; |
| |
| //////////////////////////////////////////////////////////////// |
| // pw_status-related types |
| |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::Status}. |
| template <> |
| struct ArbitraryImpl<Status> { |
| auto operator()() { |
| return ConstructorOf<Status>( |
| Map([](int code) { return static_cast<pw_Status>(code); }, |
| InRange<int>(PW_STATUS_OK, PW_STATUS_LAST))); |
| } |
| }; |
| |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::StatusWithSize}. |
| template <> |
| struct ArbitraryImpl<StatusWithSize> { |
| auto operator()() { |
| return ConstructorOf<StatusWithSize>(Arbitrary<Status>(), |
| Arbitrary<size_t>()); |
| } |
| }; |
| |
| /// Like @cpp_func{pw::fuzzer::Arbitrary<Status>}, except that |
| /// @cpp_func{pw::OkStatus} is filtered out. |
| inline auto NonOkStatus() { |
| return ConstructorOf<pw::Status>( |
| Map([](int code) { return static_cast<pw_Status>(code); }, |
| InRange<int>(PW_STATUS_CANCELLED, PW_STATUS_UNAUTHENTICATED))); |
| } |
| |
| //////////////////////////////////////////////////////////////// |
| // pw_result-related types |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::Result}s. |
| /// |
| /// The value produced may either be value produced by the given domain, or a |
| /// @cpp_class{pw::Status} indicating an error. |
| /// |
| /// Alternatively, you can use `Arbitrary<Result<T>>`. |
| /// |
| /// @param[in] inner Domain that produces values of type `T`. |
| /// |
| /// @retval Domain that produces `Result<T>`s. |
| template <int&... ExplicitArgumentBarrier, typename Inner> |
| auto ResultOf(Inner inner) { |
| return Map( |
| [](std::optional<typename Inner::value_type> value, Status status) { |
| if (value) { |
| return Result<typename Inner::value_type>(*value); |
| } |
| return Result<typename Inner::value_type>(status); |
| }, |
| OptionalOf(std::move(inner)), |
| NonOkStatus()); |
| } |
| |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::Result}. |
| template <typename T> |
| struct ArbitraryImpl<Result<T>> { |
| auto operator()() { return ResultOf(Arbitrary<T>()); } |
| }; |
| |
| //////////////////////////////////////////////////////////////// |
| // pw_containers-related types |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::Vector}s. |
| /// |
| /// Use this in place of `fuzztest::VectorOf`. The vector's maximum size is set |
| /// by the template parameter. |
| /// |
| /// Alternatively, you can use `Arbitrary<Vector<T, kMaxSize>>`. |
| /// |
| /// @param[in] inner Domain that produces values of type `T`. |
| /// |
| /// @retval Domain that produces `Vector<T>`s. |
| template <size_t kMaxSize, int&... ExplicitArgumentBarrier, typename Inner> |
| auto VectorOf(Inner inner) { |
| return fuzztest::ContainerOf<Vector<typename Inner::value_type, kMaxSize>>( |
| inner) |
| .WithMaxSize(kMaxSize); |
| } |
| |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::Vector}. |
| template <typename T, size_t kMaxSize> |
| struct ArbitraryImpl<Vector<T, kMaxSize>> { |
| auto operator()() { return VectorOf<kMaxSize>(Arbitrary<T>()); } |
| }; |
| |
| /// Like `fuzztest::UniqueElementsVectorOf`, but uses a @cpp_class{pw::Vector} |
| /// in place of a `std::vector`. |
| /// |
| /// @param[in] inner Domain the produces values for the vector. |
| /// |
| /// @retval Domain that produces `@cpp_class{pw::Vector}`s. |
| template <size_t kMaxSize, int&... ExplicitArgumentBarrier, typename Inner> |
| auto UniqueElementsVectorOf(Inner inner) { |
| return UniqueElementsContainerOf< |
| Vector<typename Inner::value_type, kMaxSize>>(std::move(inner)); |
| } |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::containers::Pair}s. |
| /// |
| /// Use this in place of `fuzztest::PairOf` when working with |
| /// @cpp_class{pw::containers::FlatMap}s. |
| /// |
| /// Alternatively, you can use `Arbitrary<pw::containers::Pair<K, V>>`. |
| /// |
| /// @param[in] keys Domain that produces values of type `K`. |
| /// @param[in] values Domain that produces values of type `V`. |
| /// |
| /// @retval Domain that produces `containers::Pair<T>`s. |
| template <int&... ExplicitArgumentBarrier, |
| typename KeyDomain, |
| typename ValueDomain> |
| auto FlatMapPairOf(KeyDomain keys, ValueDomain values) { |
| return StructOf<containers::Pair<typename KeyDomain::value_type, |
| typename ValueDomain::value_type>>( |
| std::move(keys), std::move(values)); |
| } |
| |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::containers::Pair}. |
| template <typename K, typename V> |
| struct ArbitraryImpl<containers::Pair<K, V>> { |
| auto operator()() { return FlatMapPairOf(Arbitrary<K>(), Arbitrary<V>()); } |
| }; |
| |
| /// Transforms a domain that produces containers of keys and values into a |
| /// domain that produces @cpp_class{pw::containers::FlatMap}s |
| /// |
| /// This method can be used to apply additional constraints to the set of keys |
| /// and/or values overall, e.g. by requiring all keys to be unique. |
| /// |
| /// @param[in] keys Domain that produces containers of keys. |
| /// @param[in] values Domain that produces containers of values. |
| /// |
| /// @retval Domain that produces `containers::Pair<T>`s. |
| template <size_t kArraySize, |
| int&... ExplicitArgumentBarrier, |
| typename KeyDomain, |
| typename ValueDomain> |
| auto MapToFlatMap(KeyDomain keys, ValueDomain values) { |
| using ContainerK = typename KeyDomain::value_type; |
| using ContainerV = typename ValueDomain::value_type; |
| static_assert(internal::IsContainer<ContainerK>::value); |
| static_assert(internal::IsContainer<ContainerV>::value); |
| using K = typename ContainerK::value_type; |
| using V = typename ContainerV::value_type; |
| return Map( |
| [](const ContainerK& keys_c, const ContainerV& vals_c) { |
| auto key = keys_c.begin(); |
| auto val = vals_c.begin(); |
| std::array<containers::Pair<K, V>, kArraySize> pairs; |
| for (auto& item : pairs) { |
| PW_ASSERT(key != keys_c.end()); |
| PW_ASSERT(val != vals_c.end()); |
| item.first = *key++; |
| item.second = *val++; |
| } |
| return pairs; |
| }, |
| std::move(keys), |
| std::move(values)); |
| } |
| |
| /// Returns a FuzzTest domain that produces |
| /// @cpp_class{pw::containers::FlatMap}s. |
| /// |
| /// Use this in place of `fuzztest::MapOf` and/or `fuzztest::UnorderedMapOf`. |
| /// The map's size is set by the template parameter. The map is populated by |
| /// pairs of values from the given `KeyDomain` and `ValueDomain`. |
| /// |
| /// Alternatively, you can use `Arbitrary<FlatMap<K, V, kArraySize>>`. |
| /// |
| /// Note that neither approach returns a domain that produces `FlatMap<K< V>`s. |
| /// Such a domain is infeasible, since `FlatMap<K, V>`s are not movable or |
| /// copyable. Instead, these functions return domains that produce arrays of |
| /// `Pair<K, V>`s that can be implicitly converted to `FlatMap<K, V>`s. |
| /// |
| /// @param[in] keys Domain that produces map keys. |
| /// @param[in] values Domain that produces map values. |
| /// |
| /// @retval Domain that produces `std::array<T, kArraySize>`s. |
| template <size_t kArraySize, |
| int&... ExplicitArgumentBarrier, |
| typename KeyDomain, |
| typename ValueDomain> |
| auto FlatMapOf(KeyDomain keys, ValueDomain values) { |
| return ArrayOf<kArraySize>(FlatMapPairOf(std::move(keys), std::move(values))); |
| } |
| |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::containers::FlatMap}. |
| template <typename K, typename V, size_t kArraySize> |
| struct ArbitraryImpl<containers::FlatMap<K, V, kArraySize>> { |
| auto operator()() { |
| return FlatMapOf<kArraySize>(Arbitrary<K>(), Arbitrary<V>()); |
| } |
| }; |
| |
| /// Implementation of @cpp_func{pw::fuzzer::ContainerOf} for |
| /// @cpp_class{pw::containers::FlatMap}. |
| /// |
| /// Since flat maps have a static capacity, the returned domains do not produce |
| /// FuzzTest containers, but aggregates. As a result, container methods such as |
| /// `WithMaxSize` cannot be applied. Use @cpp_func{pw::fuzzer::MapToFlatMap} |
| /// instead to apply constraints to the set of keys and values. |
| /// |
| /// @param[in] inner Domain the produces @cpp_class{pw::containers::Pair}s. |
| /// |
| /// @retval Domain that produces `@cpp_class{pw::containers::FlatMap}`s. |
| template <typename K, typename V, size_t kArraySize> |
| struct ContainerOfImpl<containers::FlatMap<K, V, kArraySize>> { |
| template <int&... ExplicitArgumentBarrier, typename Inner> |
| auto operator()(Inner inner) { |
| static_assert( |
| std::is_same_v<typename Inner::value_type, containers::Pair<K, V>>, |
| "The domain passed to `pw::fuzzer::ContainerOf<FlatMap<K, V>>` must " |
| "produce `pw::containers::Pair<K, V>`s. An example of a valid domain is" |
| "`pw::fuzzer::FlatMapPairOf(FooDomain<K>(), BarDomain<V>())`"); |
| return ArrayOf<kArraySize>(std::move(inner)); |
| } |
| }; |
| |
| /// Transforms a domain that produces containers into a domain that produces |
| /// @cpp_class{pw::BasicInlineDeque}s. |
| /// |
| /// The domains returned by @cpp_func{pw::fuzzer::BasicDequeOf} and |
| /// `Arbitrary<BasicInlineDeque>` do not create FuzzTest containers. This method |
| /// can be used to apply container methods such as `WithMinSize` or |
| /// `UniqueElementsContainerOf` before building a deque from that container. |
| /// |
| /// @param[in] inner Domain that produces containers. |
| /// |
| /// @retval Domain that produces `@cpp_class{pw::BasicInlineDeque}`s. |
| template <typename SizeType, |
| size_t kCapacity, |
| int&... ExplicitArgumentBarrier, |
| typename Inner> |
| auto MapToBasicDeque(Inner inner) { |
| using Container = typename Inner::value_type; |
| static_assert(internal::IsContainer<Container>::value); |
| using T = typename Container::value_type; |
| return Map( |
| [](const Container& items) { |
| return BasicInlineDeque<T, SizeType, kCapacity>(items.begin(), |
| items.end()); |
| }, |
| std::move(inner)); |
| } |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::BasicInlineDeque}s. |
| /// |
| /// Use this or @cpp_func{pw::fuzzer::DequeOf} in place of `fuzztest::DequeOf`. |
| /// The deque's maximum size is set by the template parameter. |
| /// |
| /// Alternatively, you can use `Arbitrary<BasicInlineDeque<T, kCapacity>>`. |
| /// |
| /// @param[in] inner Domain that produces values of type `T`. |
| /// |
| /// @retval Domain that produces values of type |
| /// `BasicInlineDeque<T, SizeType, kCapacity>`. |
| template <typename SizeType, |
| size_t kCapacity, |
| int&... ExplicitArgumentBarrier, |
| typename Inner> |
| auto BasicDequeOf(Inner inner) { |
| return MapToBasicDeque<SizeType, kCapacity>( |
| VectorOf<kCapacity>(std::move(inner))); |
| } |
| |
| // BasicDequeFrom(VectorOf<kCapacity>(Arbitrary<int>())) |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::BasicInlineDeque}. |
| template <typename T, typename SizeType, size_t kCapacity> |
| struct ArbitraryImpl<BasicInlineDeque<T, SizeType, kCapacity>> { |
| auto operator()() { |
| return BasicDequeOf<SizeType, kCapacity>(Arbitrary<T>()); |
| } |
| }; |
| |
| /// Implementation of @cpp_func{pw::fuzzer::ContainerOf} for |
| /// @cpp_class{pw::containers::BasicInlineDeque}. |
| /// |
| /// Since inline deques have a static capacity, the returned domains do not |
| /// produce FuzzTest containers, but aggregates. As a result, container methods |
| /// such as `WithMaxSize` cannot be applied. Instead, use |
| /// @cpp_func{pw::fuzzer::MapToDeque} or @cpp_func{pw::fuzzer::MapToBasicDeque} |
| /// to apply constraints to the set of keys and values. |
| /// |
| /// @param[in] inner Domain the produces values of type `T`. |
| /// |
| /// @retval Domain that produces `@cpp_class{pw::BasicInlineDeque}`s. |
| template <typename T, typename SizeType, size_t kCapacity> |
| struct ContainerOfImpl<BasicInlineDeque<T, SizeType, kCapacity>> { |
| template <int&... ExplicitArgumentBarrier, typename Inner> |
| auto operator()(Inner inner) { |
| return BasicDequeOf<SizeType, kCapacity>(std::move(inner)); |
| } |
| }; |
| |
| /// Transforms a domain that produces containers into a domain that produces |
| /// @cpp_class{pw::InlineDeque}s. |
| /// |
| /// The domains returned by @cpp_func{pw::fuzzer::equeOf} and |
| /// `Arbitrary<InlineDeque>` do not create FuzzTest containers. This method |
| /// can be used to apply container methods such as `WithMinSize` or |
| /// `UniqueElementsContainerOf` before building a deque from that container. |
| /// |
| /// @param[in] inner Domain that produces containers. |
| /// |
| /// @retval Domain that produces `@cpp_class{pw::InlineDeque}`s. |
| template <size_t kCapacity, int&... ExplicitArgumentBarrier, typename Inner> |
| auto MapToDeque(Inner inner) { |
| return MapToBasicDeque<uint16_t, kCapacity>(std::move(inner)); |
| } |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineDeque}s. |
| /// |
| /// Use this or @cpp_func{pw::fuzzer::BasicDequeOf} in place of |
| /// `fuzztest::DequeOf`. The deque's maximum size is set by the template |
| /// parameter. |
| /// |
| /// Alternatively, you can use `Arbitrary<InlineDeque<T, kCapacity>>`. |
| /// |
| /// @param[in] inner Domain that produces values of type `T`. |
| /// |
| /// @retval Domain that produces values of type `InlineDeque<T, kCapacity>`. |
| template <size_t kCapacity, int&... ExplicitArgumentBarrier, typename Inner> |
| auto DequeOf(Inner inner) { |
| return BasicDequeOf<uint16_t, kCapacity>(std::move(inner)); |
| } |
| |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::InlineDeque}. |
| template <typename T, size_t kCapacity> |
| struct ArbitraryImpl<InlineDeque<T, kCapacity>> { |
| auto operator()() { return DequeOf<kCapacity>(Arbitrary<T>()); } |
| }; |
| |
| /// Transforms a domain that produces containers into a domain that produces |
| /// @cpp_class{pw::BasicInlineQueue}s. |
| /// |
| /// The domains returned by @cpp_func{pw::fuzzer::BasicQueueOf} and |
| /// `Arbitrary<BasicInlineQueue>` do not create FuzzTest containers. This method |
| /// can be used to apply container methods such as `WithMinSize` or |
| /// `UniqueElementsContainerOf` before building a queue from that container. |
| /// |
| /// @param[in] inner Domain that produces containers. |
| /// |
| /// @retval Domain that produces `@cpp_class{pw::BasicInlineQueue}`s. |
| template <typename SizeType, |
| size_t kCapacity, |
| int&... ExplicitArgumentBarrier, |
| typename Inner> |
| auto MapToBasicQueue(Inner inner) { |
| using Container = typename Inner::value_type; |
| static_assert(internal::IsContainer<Container>::value); |
| using T = typename Container::value_type; |
| return Map( |
| [](const Container& items) { |
| return BasicInlineQueue<T, SizeType, kCapacity>(items.begin(), |
| items.end()); |
| }, |
| std::move(inner)); |
| } |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::BasicInlineQueue}s. |
| /// |
| /// Use this, @cpp_func{pw::fuzzer::QueueOf}, or |
| /// @cpp_func{pw::fuzzer::ScopedListOf} in place of `fuzztest::ListOf`. The |
| /// queue's maximum size is set by the template parameter. |
| /// |
| /// Alternatively, you can use `Arbitrary<BasicInlineQueue<T, kCapacity>>`. |
| /// |
| /// @param[in] inner Domain that produces values of type `T`. |
| /// |
| /// @retval Domain that produces values of type |
| /// `BasicInlineQueue<T, SizeType, kCapacity>`. |
| template <typename SizeType, |
| size_t kCapacity, |
| int&... ExplicitArgumentBarrier, |
| typename Inner> |
| auto BasicQueueOf(Inner inner) { |
| return MapToBasicQueue<SizeType, kCapacity>( |
| VectorOf<kCapacity>(std::move(inner))); |
| } |
| |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::BasicInlineQueue}. |
| template <typename T, typename SizeType, size_t kCapacity> |
| struct ArbitraryImpl<BasicInlineQueue<T, SizeType, kCapacity>> { |
| auto operator()() { |
| return BasicQueueOf<SizeType, kCapacity>(Arbitrary<T>()); |
| } |
| }; |
| |
| /// Implementation of @cpp_func{pw::fuzzer::ContainerOf} for |
| /// @cpp_class{pw::containers::BasicInlineQueue}. |
| /// |
| /// Since inline queues have a static capacity, the returned domains do not |
| /// produce FuzzTest containers, but aggregates. As a result, container methods |
| /// such as `WithMaxSize` cannot be applied. Instead, use |
| /// @cpp_func{pw::fuzzer::MapToQueue} or @cpp_func{pw::fuzzer::MapToBasicQueue} |
| /// to apply constraints to the set of keys and values. |
| /// |
| /// @param[in] inner Domain the produces values of type `T`. |
| /// |
| /// @retval Domain that produces `@cpp_class{pw::BasicInlineQueue}`s. |
| template <typename T, typename SizeType, size_t kCapacity> |
| struct ContainerOfImpl<BasicInlineQueue<T, SizeType, kCapacity>> { |
| template <int&... ExplicitArgumentBarrier, typename Inner> |
| auto operator()(Inner inner) { |
| return BasicQueueOf<SizeType, kCapacity>(std::move(inner)); |
| } |
| }; |
| |
| /// Transforms a domain that produces containers into a domain that produces |
| /// @cpp_class{pw::InlineQueue}s. |
| /// |
| /// The domains returned by @cpp_func{pw::fuzzer::QueueOf} and |
| /// `Arbitrary<InlineQueue>` do not create FuzzTest containers. This method |
| /// can be used to apply container methods such as `WithMinSize` or |
| /// `UniqueElementsContainerOf` before building a queue from that container. |
| /// |
| /// @param[in] inner Domain that produces containers. |
| /// |
| /// @retval Domain that produces `@cpp_class{pw::InlineQueue}`s. |
| template <size_t kCapacity, int&... ExplicitArgumentBarrier, typename Inner> |
| auto MapToQueue(Inner inner) { |
| return MapToBasicQueue<uint16_t, kCapacity>(std::move(inner)); |
| } |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineQueue}s. |
| /// |
| /// Use this, @cpp_func{pw::fuzzer::BasicQueueOf}, or |
| /// @cpp_func{pw::fuzzer::ScopedListOf} in place of `fuzztest::ListOf`. The |
| /// queue's maximum size is set by the template parameter. |
| /// |
| /// Alternatively, you can use `Arbitrary<InlineQueue<T, kCapacity>>`. |
| /// |
| /// @param[in] inner Domain that produces values of type `T`. |
| /// |
| /// @retval Domain that produces values of type `InlineQueue<T, kCapacity>`. |
| template <size_t kCapacity, int&... ExplicitArgumentBarrier, typename Inner> |
| auto QueueOf(Inner inner) { |
| return BasicQueueOf<uint16_t, kCapacity>(std::move(inner)); |
| } |
| |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::InlineQueue}. |
| template <typename T, size_t kCapacity> |
| struct ArbitraryImpl<InlineQueue<T, kCapacity>> { |
| auto operator()() { return QueueOf<kCapacity>(Arbitrary<T>()); } |
| }; |
| |
| /// Associates an `IntrusiveList<T>` with a `Vector<T>` that stores its `Item`s. |
| /// |
| /// The `Item`s are constructed from a sequence of argument tuples passed to |
| // constructor. |
| template <typename T, size_t kMaxSize> |
| class ScopedList { |
| public: |
| ~ScopedList() { list_.clear(); } |
| |
| template <int&... ExplicitArgumentBarrier, typename Tuple> |
| explicit ScopedList(const Vector<Tuple>& arg_tuples) { |
| for (const auto& arg_tuple : arg_tuples) { |
| items_.emplace_back(std::make_from_tuple<T>(arg_tuple)); |
| list_.push_back(items_.back()); |
| } |
| } |
| |
| ScopedList(const ScopedList& other) = delete; |
| ScopedList& operator=(const ScopedList& other) = delete; |
| |
| ScopedList(ScopedList&& other) { *this = std::move(other); } |
| ScopedList& operator=(ScopedList&& other) { |
| list_.clear(); |
| other.list_.clear(); |
| items_ = std::move(other.items_); |
| list_.assign(items_.begin(), items_.end()); |
| return *this; |
| } |
| |
| const IntrusiveList<T>& list() const { return list_; } |
| |
| private: |
| IntrusiveList<T> list_; |
| Vector<T, kMaxSize> items_; |
| }; |
| |
| /// Transforms a domain that produces containers into a domain that produces |
| /// @cpp_class{pw::fuzzer::ScopedList}s. |
| /// |
| /// The domains returned by @cpp_func{pw::fuzzer::ScopedListOf} do not create |
| /// FuzzTest containers. This method can be used to apply container methods such |
| /// as `WithMinSize` or `UniqueElementsContainerOf` before building an intrusive |
| /// list from that container. |
| /// |
| /// @param[in] inner Domain that produces containers. |
| /// |
| /// @retval Domain that produces `@cpp_class{pw::fuzzer::ScopedList}`s. |
| template <typename T, |
| size_t kMaxSize, |
| int&... ExplicitArgumentBarrier, |
| typename Inner> |
| auto MapToScopedList(Inner inner) { |
| using Container = typename Inner::value_type; |
| static_assert(internal::IsContainer<Container>::value); |
| using Tuple = typename Container::value_type; |
| static_assert( |
| std::is_same_v<T, decltype(std::make_from_tuple<T>(Tuple()))>, |
| "The domain passed to `pw::fuzzer::MapToScopedList<T, kMaxSize>>` must " |
| "produce `std::tuple`s of constructor arguments for `T`, e.g. using " |
| "`pw::fuzzer::TupleOf`."); |
| return ConstructorOf<ScopedList<T, kMaxSize>>(std::move(inner)); |
| } |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::fuzzer::ScopedList}s. |
| /// |
| /// Use this, @cpp_func{pw::fuzzer::BasicQueueOf}, or |
| /// @cpp_func{pw::fuzzer::QueueOf} in place of `fuzztest::ListOf`. The list's |
| /// maximum size is set by the template parameter. |
| /// |
| /// @param[in] inner... Domains that produces `IntrusiveList<T>::Item`s. |
| /// |
| /// @retval Domain that produces `ScopedList<T, kMaxSize>`s. |
| template <typename T, |
| size_t kMaxSize, |
| int&... ExplicitArgumentBarrier, |
| typename... Inner> |
| auto ScopedListOf(Inner... inner) { |
| return MapToScopedList<T, kMaxSize>( |
| VectorOf<kMaxSize>(TupleOf(std::move(inner)...))); |
| } |
| |
| //////////////////////////////////////////////////////////////// |
| // pw_string-related types |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineBasicString}s. |
| /// |
| /// Use this in place of `fuzztest::StringOf`. The characters of the string |
| /// are drawn from the given domain. The string capacity is given by the |
| /// template parameter. |
| /// |
| /// Alternatively, you can use `Arbitrary<InlineString<kCapacity>>`. |
| /// |
| /// @param[in] inner Domain that produces values of a character type. |
| /// |
| /// @retval Domain that produces `InlineBasicString<kCapacity>`s. |
| template <size_t kCapacity, int&... ExplicitArgumentBarrier, typename Inner> |
| auto StringOf(Inner inner) { |
| return ContainerOf<InlineBasicString<typename Inner::value_type, kCapacity>>( |
| inner) |
| .WithMaxSize(kCapacity); |
| } |
| |
| /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for |
| /// @cpp_class{pw::InlineBasicString}. |
| template <typename T, size_t kCapacity> |
| struct ArbitraryImpl<InlineBasicString<T, kCapacity>> { |
| auto operator()() { return StringOf<kCapacity>(Arbitrary<T>()); } |
| }; |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineString}s. |
| /// |
| /// Use this in place of `fuzztest::String`. The string capacity is given by the |
| /// template parameter. |
| /// |
| /// @retval Domain that produces `InlineString<kCapacity>`s. |
| template <size_t kCapacity> |
| auto String() { |
| return StringOf<kCapacity>(Arbitrary<char>()); |
| } |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineString}s |
| /// containing only ASCII characters. |
| /// |
| /// Use this in place of `fuzztest::AsciiString`. The string capacity is given |
| /// by the template parameter. |
| /// |
| /// @retval Domain that produces `InlineString<kCapacity>`s. |
| template <size_t kCapacity> |
| auto AsciiString() { |
| return StringOf<kCapacity>(AsciiChar()); |
| } |
| |
| /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineString}s |
| /// containing only printable ASCII characters. |
| /// |
| /// Use this in place of `fuzztest::PrintableAsciiString`. The string capacity |
| /// is given by the template parameter. |
| /// |
| /// @retval Domain that produces printable `InlineString<kCapacity>`s. |
| template <size_t kCapacity> |
| auto PrintableAsciiString() { |
| return StringOf<kCapacity>(PrintableAsciiChar()); |
| } |
| |
| } // namespace pw::fuzzer |