xref: /aosp_15_r20/external/pigweed/pw_fuzzer/public/pw_fuzzer/fuzztest.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 /// @file fuzztest.h
17 /// Pigweed interface to FuzzTest
18 ///
19 /// @rst
20 /// This header exposes the portion of the FuzzTest interface that only depends
21 /// on permitted C++ standard library `headers`_, including `macros`_ and
22 /// `domains`_.
23 ///
24 /// It also extends the interface to provide domains for common Pigweed types,
25 /// such as those from the following modules:
26 ///
27 /// * :ref:`module-pw_containers`
28 /// * :ref:`module-pw_result`
29 /// * :ref:`module-pw_status`
30 /// * :ref:`module-pw_string`
31 ///
32 /// .. _domains:
33 ///    https://github.com/google/fuzztest/blob/main/doc/domains-reference.md
34 /// .. _headers: https://pigweed.dev/docs/style_guide.html#permitted-headers
35 /// .. _macros:
36 ///    https://github.com/google/fuzztest/blob/main/doc/fuzz-test-macro.md
37 /// @endrst
38 
39 #include "pw_containers/flat_map.h"
40 #include "pw_containers/inline_deque.h"
41 #include "pw_containers/inline_queue.h"
42 #include "pw_containers/intrusive_list.h"
43 #include "pw_containers/vector.h"
44 #include "pw_fuzzer/internal/fuzztest.h"
45 #include "pw_result/result.h"
46 #include "pw_status/status.h"
47 #include "pw_status/status_with_size.h"
48 #include "pw_string/string.h"
49 
50 namespace pw::fuzzer {
51 
52 template <typename T>
53 using Domain = fuzztest::Domain<T>;
54 
55 ////////////////////////////////////////////////////////////////
56 // Arbitrary domains
57 // Additional specializations are provided with the Pigweed domains.
58 
59 /// @struct ArbitraryImpl
60 /// @fn Arbitrary
61 ///
62 /// Produces values for fuzz target function parameters.
63 ///
64 /// This defines a new template rather than using the `fuzztest` one directly in
65 /// order to facilitate specializations for Pigweed types.
66 ///
67 /// See
68 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#arbitrary-domains
69 template <typename T, typename = void>
70 struct ArbitraryImpl {
operatorArbitraryImpl71   auto operator()() { return fuzztest::Arbitrary<T>(); }
72 };
73 template <typename T>
Arbitrary()74 auto Arbitrary() {
75   return ArbitraryImpl<T>()();
76 }
77 
78 ////////////////////////////////////////////////////////////////
79 // Numerical domains
80 
81 /// Produces values from a closed interval.
82 ///
83 /// See
84 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains
85 using fuzztest::InRange;
86 
87 /// Like Arbitrary<T>(), but does not produce the zero value.
88 ///
89 /// See
90 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains
91 using fuzztest::NonZero;
92 
93 /// Produces numbers greater than zero.
94 ///
95 /// See
96 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains
97 using fuzztest::Positive;
98 
99 /// Produces the zero value and numbers greater than zero.
100 ///
101 /// See
102 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains
103 using fuzztest::NonNegative;
104 
105 /// Produces numbers less than zero.
106 ///
107 /// See
108 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains
109 using fuzztest::Negative;
110 
111 /// Produces the zero value and numbers less than zero.
112 ///
113 /// See
114 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains
115 using fuzztest::NonPositive;
116 
117 /// Produces floating points numbers that are neither infinity nor NaN.
118 ///
119 /// See
120 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains
121 using fuzztest::Finite;
122 
123 ////////////////////////////////////////////////////////////////
124 // Character domains
125 
126 /// Produces any char except '\0'.
127 ///
128 /// See
129 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains
130 using fuzztest::NonZeroChar;
131 
132 /// Alias for `InRange('0', '9')`.
133 ///
134 /// See
135 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains
136 using fuzztest::NumericChar;
137 
138 /// Alias for `InRange('a', 'z')`.
139 ///
140 /// See
141 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains
142 using fuzztest::LowerChar;
143 
144 /// Alias for `InRange('A', 'Z')`.
145 ///
146 /// See
147 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains
148 using fuzztest::UpperChar;
149 
150 /// Alias for `OneOf(LowerChar(), UpperChar())`.
151 ///
152 /// See
153 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains
154 using fuzztest::AlphaChar;
155 
156 /// Alias for `OneOf(AlphaChar(), NumericChar())`.
157 ///
158 /// See
159 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains
160 using fuzztest::AlphaNumericChar;
161 
162 /// Produces printable characters (`InRange<char>(32, 126)`).
163 ///
164 /// See
165 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains
166 using fuzztest::PrintableAsciiChar;
167 
168 /// Produces ASCII characters (`InRange<char>(0, 127)`).
169 ///
170 /// See
171 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains
172 using fuzztest::AsciiChar;
173 
174 ////////////////////////////////////////////////////////////////
175 // Regular expression domains
176 
177 // TODO: b/285775246 - Add support for `fuzztest::InRegexp`.
178 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#inregexp-domains
179 // using fuzztest::InRegexp;
180 
181 ////////////////////////////////////////////////////////////////
182 // Enumerated domains
183 
184 /// Produces values from an enumerated set.
185 ///
186 /// See
187 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#elementof-domains
188 using fuzztest::ElementOf;
189 
190 /// Produces combinations of binary flags from a provided list.
191 ///
192 /// See
193 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#bitflagcombinationof-domains
194 using fuzztest::BitFlagCombinationOf;
195 
196 ////////////////////////////////////////////////////////////////
197 // Container domains
198 
199 /// @struct ContainerOfImpl
200 /// @fn ContainerOf
201 ///
202 /// Produces containers of elements provided by inner domains.
203 ///
204 /// This defines a new template rather than using the `fuzztest` one directly in
205 /// order to specify the static container capacity as part of the container
206 /// type.
207 ///
208 /// See
209 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators
210 template <typename Container, typename = void>
211 struct ContainerOfImpl {
212   template <int&... ExplicitArgumentBarrier, typename Inner>
operatorContainerOfImpl213   auto operator()(Inner inner) {
214     return fuzztest::ContainerOf<Container>(std::move(inner))
215         .WithMaxSize(Container{}.max_size());
216   }
217 };
218 template <typename Container, int&... ExplicitArgumentBarrier, typename Inner>
ContainerOf(Inner inner)219 auto ContainerOf(Inner inner) {
220   return ContainerOfImpl<Container>()(std::move(inner));
221 }
222 
223 namespace internal {
224 
225 template <typename T, typename = void>
226 struct IsContainer : std::false_type {};
227 
228 template <typename T>
229 struct IsContainer<T, std::void_t<decltype(T().begin(), T().end())>>
230     : std::true_type {};
231 
232 }  // namespace internal
233 
234 /// Produces containers of at least one element provided by inner domains.
235 ///
236 /// The container type is given by a template parameter.
237 ///
238 /// See
239 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators
240 using fuzztest::NonEmpty;
241 
242 /// @struct UniqueElementsContainerOfImpl
243 /// @fn UniqueElementsContainerOf
244 ///
245 /// Produces containers of unique elements provided by inner domains.
246 ///
247 /// This defines a new template rather than using the `fuzztest` one directly in
248 /// order to specify the static container capacity as part of the container
249 /// type.
250 ///
251 /// See
252 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators
253 template <typename Container, typename = void>
254 struct UniqueElementsContainerOfImpl {
255   template <int&... ExplicitArgumentBarrier, typename Inner>
256   auto operator()(Inner inner) {
257     return fuzztest::UniqueElementsContainerOf<Container>(std::move(inner))
258         .WithMaxSize(Container{}.max_size());
259   }
260 };
261 template <typename Container, int&... ExplicitArgumentBarrier, typename Inner>
262 auto UniqueElementsContainerOf(Inner inner) {
263   return UniqueElementsContainerOfImpl<Container>()(std::move(inner));
264 }
265 
266 ////////////////////////////////////////////////////////////////
267 // Aggregate domains
268 
269 /// Produces std::array<T>s of elements provided by inner domains.
270 ///
271 /// See
272 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators
273 using fuzztest::ArrayOf;
274 
275 /// Specializes @cpp_class{pw::fuzzer::ContainerOfImpl} for arrays, which do not
276 /// need a maximum size applied.
277 ///
278 /// @param[in]  inner   Domain the produces values of type `T`.
279 ///
280 /// @retval     Domain that produces `std::array<T, kArraySize>`s.
281 template <typename T, size_t N>
282 struct ContainerOfImpl<std::array<T, N>> {
283   template <int&... ExplicitArgumentBarrier, typename Inner>
284   auto operator()(Inner inner) {
285     return fuzztest::ContainerOf<std::array<T, N>>(std::move(inner));
286   }
287 };
288 
289 /// Produces user-defined structs.
290 ///
291 /// The struct type is given by a template parameter.
292 ///
293 /// See
294 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#structof
295 using fuzztest::StructOf;
296 
297 /// Produces user-defined objects.
298 ///
299 /// The class type is given by a template parameter.
300 ///
301 /// See
302 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#constructorof
303 using fuzztest::ConstructorOf;
304 
305 /// Produces `std::pair<T1, T2>`s from provided inner domains.
306 ///
307 /// See
308 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#pairof
309 using fuzztest::PairOf;
310 
311 /// Produces `std::tuple<T...>`s from provided inner domains.
312 ///
313 /// See
314 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#tupleof
315 using fuzztest::TupleOf;
316 
317 /// Produces `std::variant<T...>` from provided inner domains.
318 ///
319 /// See
320 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#variantof
321 using fuzztest::VariantOf;
322 
323 /// Produces `std::optional<T>` from provided inner domain.
324 ///
325 /// See
326 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#optionalof
327 using fuzztest::OptionalOf;
328 
329 /// Produces `std::optional<T>` from provided inner domain that is null.
330 ///
331 /// See
332 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#optionalof
333 using fuzztest::NullOpt;
334 
335 /// Produces `std::optional<T>` from provided inner domain that is not null.
336 ///
337 /// See
338 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#optionalof
339 using fuzztest::NonNull;
340 
341 ////////////////////////////////////////////////////////////////
342 // Other miscellaneous domains
343 
344 /// Produces values by choosing between provided inner domains.
345 ///
346 /// See
347 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#oneof
348 using fuzztest::OneOf;
349 
350 /// Produces values equal to the provided value.
351 ///
352 /// See
353 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#oneof
354 using fuzztest::Just;
355 
356 /// Produces values by applying a function to values from provided domains.
357 ///
358 /// See
359 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#map
360 using fuzztest::Map;
361 
362 /// Creates a domain by applying a function to values from provided domains.
363 ///
364 /// See
365 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#flatmap
366 using fuzztest::FlatMap;
367 
368 /// Produces values from a provided domain that will cause a provided predicate
369 /// to return true.
370 ///
371 /// See
372 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#filter
373 using fuzztest::Filter;
374 
375 ////////////////////////////////////////////////////////////////
376 // pw_status-related types
377 
378 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
379 /// @cpp_class{pw::Status}.
380 template <>
381 struct ArbitraryImpl<Status> {
382   auto operator()() {
383     return ConstructorOf<Status>(
384         Map([](int code) { return static_cast<pw_Status>(code); },
385             InRange<int>(PW_STATUS_OK, PW_STATUS_LAST)));
386   }
387 };
388 
389 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
390 /// @cpp_class{pw::StatusWithSize}.
391 template <>
392 struct ArbitraryImpl<StatusWithSize> {
393   auto operator()() {
394     return ConstructorOf<StatusWithSize>(Arbitrary<Status>(),
395                                          Arbitrary<size_t>());
396   }
397 };
398 
399 /// Like @cpp_func{pw::fuzzer::Arbitrary<Status>}, except that
400 /// @cpp_func{pw::OkStatus} is filtered out.
401 inline auto NonOkStatus() {
402   return ConstructorOf<pw::Status>(
403       Map([](int code) { return static_cast<pw_Status>(code); },
404           InRange<int>(PW_STATUS_CANCELLED, PW_STATUS_UNAUTHENTICATED)));
405 }
406 
407 ////////////////////////////////////////////////////////////////
408 // pw_result-related types
409 
410 /// Returns a FuzzTest domain that produces @cpp_class{pw::Result}s.
411 ///
412 /// The value produced may either be value produced by the given domain, or a
413 /// @cpp_class{pw::Status} indicating an error.
414 ///
415 /// Alternatively, you can use `Arbitrary<Result<T>>`.
416 ///
417 /// @param[in] inner  Domain that produces values of type `T`.
418 ///
419 /// @retval    Domain that produces `Result<T>`s.
420 template <int&... ExplicitArgumentBarrier, typename Inner>
421 auto ResultOf(Inner inner) {
422   return Map(
423       [](std::optional<typename Inner::value_type> value, Status status) {
424         if (value) {
425           return Result<typename Inner::value_type>(*value);
426         }
427         return Result<typename Inner::value_type>(status);
428       },
429       OptionalOf(std::move(inner)),
430       NonOkStatus());
431 }
432 
433 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
434 /// @cpp_class{pw::Result}.
435 template <typename T>
436 struct ArbitraryImpl<Result<T>> {
437   auto operator()() { return ResultOf(Arbitrary<T>()); }
438 };
439 
440 ////////////////////////////////////////////////////////////////
441 // pw_containers-related types
442 
443 /// Returns a FuzzTest domain that produces @cpp_class{pw::Vector}s.
444 ///
445 /// Use this in place of `fuzztest::VectorOf`. The vector's maximum size is set
446 /// by the template parameter.
447 ///
448 /// Alternatively, you can use `Arbitrary<Vector<T, kMaxSize>>`.
449 ///
450 /// @param[in] inner  Domain that produces values of type `T`.
451 ///
452 /// @retval    Domain that produces `Vector<T>`s.
453 template <size_t kMaxSize, int&... ExplicitArgumentBarrier, typename Inner>
454 auto VectorOf(Inner inner) {
455   return fuzztest::ContainerOf<Vector<typename Inner::value_type, kMaxSize>>(
456              inner)
457       .WithMaxSize(kMaxSize);
458 }
459 
460 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
461 /// @cpp_class{pw::Vector}.
462 template <typename T, size_t kMaxSize>
463 struct ArbitraryImpl<Vector<T, kMaxSize>> {
464   auto operator()() { return VectorOf<kMaxSize>(Arbitrary<T>()); }
465 };
466 
467 /// Like `fuzztest::UniqueElementsVectorOf`, but uses a @cpp_class{pw::Vector}
468 /// in place of a `std::vector`.
469 ///
470 /// @param[in]  inner   Domain the produces values for the vector.
471 ///
472 /// @retval     Domain that produces `@cpp_class{pw::Vector}`s.
473 template <size_t kMaxSize, int&... ExplicitArgumentBarrier, typename Inner>
474 auto UniqueElementsVectorOf(Inner inner) {
475   return UniqueElementsContainerOf<
476       Vector<typename Inner::value_type, kMaxSize>>(std::move(inner));
477 }
478 
479 /// Returns a FuzzTest domain that produces @cpp_class{pw::containers::Pair}s.
480 ///
481 /// Use this in place of `fuzztest::PairOf` when working with
482 /// @cpp_class{pw::containers::FlatMap}s.
483 ///
484 /// Alternatively, you can use `Arbitrary<pw::containers::Pair<K, V>>`.
485 ///
486 /// @param[in] keys     Domain that produces values of type `K`.
487 /// @param[in] values   Domain that produces values of type `V`.
488 ///
489 /// @retval    Domain that produces `containers::Pair<T>`s.
490 template <int&... ExplicitArgumentBarrier,
491           typename KeyDomain,
492           typename ValueDomain>
493 auto FlatMapPairOf(KeyDomain keys, ValueDomain values) {
494   return StructOf<containers::Pair<typename KeyDomain::value_type,
495                                    typename ValueDomain::value_type>>(
496       std::move(keys), std::move(values));
497 }
498 
499 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
500 /// @cpp_class{pw::containers::Pair}.
501 template <typename K, typename V>
502 struct ArbitraryImpl<containers::Pair<K, V>> {
503   auto operator()() { return FlatMapPairOf(Arbitrary<K>(), Arbitrary<V>()); }
504 };
505 
506 /// Transforms a domain that produces containers of keys and values into a
507 /// domain that produces @cpp_class{pw::containers::FlatMap}s
508 ///
509 /// This method can be used to apply additional constraints to the set of keys
510 /// and/or values overall, e.g. by requiring all keys to be unique.
511 ///
512 /// @param[in] keys     Domain that produces containers of keys.
513 /// @param[in] values   Domain that produces containers of values.
514 ///
515 /// @retval    Domain that produces `containers::Pair<T>`s.
516 template <size_t kArraySize,
517           int&... ExplicitArgumentBarrier,
518           typename KeyDomain,
519           typename ValueDomain>
520 auto MapToFlatMap(KeyDomain keys, ValueDomain values) {
521   using ContainerK = typename KeyDomain::value_type;
522   using ContainerV = typename ValueDomain::value_type;
523   static_assert(internal::IsContainer<ContainerK>::value);
524   static_assert(internal::IsContainer<ContainerV>::value);
525   using K = typename ContainerK::value_type;
526   using V = typename ContainerV::value_type;
527   return Map(
528       [](const ContainerK& keys_c, const ContainerV& vals_c) {
529         auto key = keys_c.begin();
530         auto val = vals_c.begin();
531         std::array<containers::Pair<K, V>, kArraySize> pairs;
532         for (auto& item : pairs) {
533           PW_ASSERT(key != keys_c.end());
534           PW_ASSERT(val != vals_c.end());
535           item.first = *key++;
536           item.second = *val++;
537         }
538         return pairs;
539       },
540       std::move(keys),
541       std::move(values));
542 }
543 
544 /// Returns a FuzzTest domain that produces
545 /// @cpp_class{pw::containers::FlatMap}s.
546 ///
547 /// Use this in place of `fuzztest::MapOf` and/or `fuzztest::UnorderedMapOf`.
548 /// The map's size is set by the template parameter. The map is populated by
549 /// pairs of values from the given `KeyDomain` and `ValueDomain`.
550 ///
551 /// Alternatively, you can use `Arbitrary<FlatMap<K, V, kArraySize>>`.
552 ///
553 /// Note that neither approach returns a domain that produces `FlatMap<K< V>`s.
554 /// Such a domain is infeasible, since `FlatMap<K, V>`s are not movable or
555 /// copyable. Instead, these functions return domains that produce arrays of
556 /// `Pair<K, V>`s that can be implicitly converted to `FlatMap<K, V>`s.
557 ///
558 /// @param[in] keys    Domain that produces map keys.
559 /// @param[in] values  Domain that produces map values.
560 ///
561 /// @retval    Domain that produces `std::array<T, kArraySize>`s.
562 template <size_t kArraySize,
563           int&... ExplicitArgumentBarrier,
564           typename KeyDomain,
565           typename ValueDomain>
566 auto FlatMapOf(KeyDomain keys, ValueDomain values) {
567   return ArrayOf<kArraySize>(FlatMapPairOf(std::move(keys), std::move(values)));
568 }
569 
570 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
571 /// @cpp_class{pw::containers::FlatMap}.
572 template <typename K, typename V, size_t kArraySize>
573 struct ArbitraryImpl<containers::FlatMap<K, V, kArraySize>> {
574   auto operator()() {
575     return FlatMapOf<kArraySize>(Arbitrary<K>(), Arbitrary<V>());
576   }
577 };
578 
579 /// Implementation of @cpp_func{pw::fuzzer::ContainerOf} for
580 /// @cpp_class{pw::containers::FlatMap}.
581 ///
582 /// Since flat maps have a static capacity, the returned domains do not produce
583 /// FuzzTest containers, but aggregates. As a result, container methods such as
584 /// `WithMaxSize` cannot be applied. Use @cpp_func{pw::fuzzer::MapToFlatMap}
585 /// instead to apply constraints to the set of keys and values.
586 ///
587 /// @param[in]  inner   Domain the produces @cpp_class{pw::containers::Pair}s.
588 ///
589 /// @retval     Domain that produces `@cpp_class{pw::containers::FlatMap}`s.
590 template <typename K, typename V, size_t kArraySize>
591 struct ContainerOfImpl<containers::FlatMap<K, V, kArraySize>> {
592   template <int&... ExplicitArgumentBarrier, typename Inner>
593   auto operator()(Inner inner) {
594     static_assert(
595         std::is_same_v<typename Inner::value_type, containers::Pair<K, V>>,
596         "The domain passed to `pw::fuzzer::ContainerOf<FlatMap<K, V>>` must "
597         "produce `pw::containers::Pair<K, V>`s. An example of a valid domain is"
598         "`pw::fuzzer::FlatMapPairOf(FooDomain<K>(), BarDomain<V>())`");
599     return ArrayOf<kArraySize>(std::move(inner));
600   }
601 };
602 
603 /// Transforms a domain that produces containers into a domain that produces
604 /// @cpp_class{pw::BasicInlineDeque}s.
605 ///
606 /// The domains returned by @cpp_func{pw::fuzzer::BasicDequeOf} and
607 /// `Arbitrary<BasicInlineDeque>` do not create FuzzTest containers. This method
608 /// can be used to apply container methods such as `WithMinSize` or
609 /// `UniqueElementsContainerOf` before building a deque from that container.
610 ///
611 /// @param[in] inner  Domain that produces containers.
612 ///
613 /// @retval    Domain that produces `@cpp_class{pw::BasicInlineDeque}`s.
614 template <typename SizeType,
615           size_t kCapacity,
616           int&... ExplicitArgumentBarrier,
617           typename Inner>
618 auto MapToBasicDeque(Inner inner) {
619   using Container = typename Inner::value_type;
620   static_assert(internal::IsContainer<Container>::value);
621   using T = typename Container::value_type;
622   return Map(
623       [](const Container& items) {
624         return BasicInlineDeque<T, SizeType, kCapacity>(items.begin(),
625                                                         items.end());
626       },
627       std::move(inner));
628 }
629 
630 /// Returns a FuzzTest domain that produces @cpp_class{pw::BasicInlineDeque}s.
631 ///
632 /// Use this or @cpp_func{pw::fuzzer::DequeOf} in place of `fuzztest::DequeOf`.
633 /// The deque's maximum size is set by the template parameter.
634 ///
635 /// Alternatively, you can use `Arbitrary<BasicInlineDeque<T, kCapacity>>`.
636 ///
637 /// @param[in] inner  Domain that produces values of type `T`.
638 ///
639 /// @retval    Domain that produces values of type
640 ///            `BasicInlineDeque<T, SizeType, kCapacity>`.
641 template <typename SizeType,
642           size_t kCapacity,
643           int&... ExplicitArgumentBarrier,
644           typename Inner>
645 auto BasicDequeOf(Inner inner) {
646   return MapToBasicDeque<SizeType, kCapacity>(
647       VectorOf<kCapacity>(std::move(inner)));
648 }
649 
650 // BasicDequeFrom(VectorOf<kCapacity>(Arbitrary<int>()))
651 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
652 /// @cpp_class{pw::BasicInlineDeque}.
653 template <typename T, typename SizeType, size_t kCapacity>
654 struct ArbitraryImpl<BasicInlineDeque<T, SizeType, kCapacity>> {
655   auto operator()() {
656     return BasicDequeOf<SizeType, kCapacity>(Arbitrary<T>());
657   }
658 };
659 
660 /// Implementation of @cpp_func{pw::fuzzer::ContainerOf} for
661 /// @cpp_class{pw::containers::BasicInlineDeque}.
662 ///
663 /// Since inline deques have a static capacity, the returned domains do not
664 /// produce FuzzTest containers, but aggregates. As a result, container methods
665 /// such as `WithMaxSize` cannot be applied. Instead, use
666 /// @cpp_func{pw::fuzzer::MapToDeque} or @cpp_func{pw::fuzzer::MapToBasicDeque}
667 /// to apply constraints to the set of keys and values.
668 ///
669 /// @param[in]  inner   Domain the produces values of type `T`.
670 ///
671 /// @retval     Domain that produces `@cpp_class{pw::BasicInlineDeque}`s.
672 template <typename T, typename SizeType, size_t kCapacity>
673 struct ContainerOfImpl<BasicInlineDeque<T, SizeType, kCapacity>> {
674   template <int&... ExplicitArgumentBarrier, typename Inner>
675   auto operator()(Inner inner) {
676     return BasicDequeOf<SizeType, kCapacity>(std::move(inner));
677   }
678 };
679 
680 /// Transforms a domain that produces containers into a domain that produces
681 /// @cpp_class{pw::InlineDeque}s.
682 ///
683 /// The domains returned by @cpp_func{pw::fuzzer::equeOf} and
684 /// `Arbitrary<InlineDeque>` do not create FuzzTest containers. This method
685 /// can be used to apply container methods such as `WithMinSize` or
686 /// `UniqueElementsContainerOf` before building a deque from that container.
687 ///
688 /// @param[in] inner  Domain that produces containers.
689 ///
690 /// @retval    Domain that produces `@cpp_class{pw::InlineDeque}`s.
691 template <size_t kCapacity, int&... ExplicitArgumentBarrier, typename Inner>
692 auto MapToDeque(Inner inner) {
693   return MapToBasicDeque<uint16_t, kCapacity>(std::move(inner));
694 }
695 
696 /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineDeque}s.
697 ///
698 /// Use this or @cpp_func{pw::fuzzer::BasicDequeOf} in place of
699 /// `fuzztest::DequeOf`. The deque's maximum size is set by the template
700 /// parameter.
701 ///
702 /// Alternatively, you can use `Arbitrary<InlineDeque<T, kCapacity>>`.
703 ///
704 /// @param[in] inner  Domain that produces values of type `T`.
705 ///
706 /// @retval    Domain that produces values of type `InlineDeque<T, kCapacity>`.
707 template <size_t kCapacity, int&... ExplicitArgumentBarrier, typename Inner>
708 auto DequeOf(Inner inner) {
709   return BasicDequeOf<uint16_t, kCapacity>(std::move(inner));
710 }
711 
712 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
713 /// @cpp_class{pw::InlineDeque}.
714 template <typename T, size_t kCapacity>
715 struct ArbitraryImpl<InlineDeque<T, kCapacity>> {
716   auto operator()() { return DequeOf<kCapacity>(Arbitrary<T>()); }
717 };
718 
719 /// Transforms a domain that produces containers into a domain that produces
720 /// @cpp_class{pw::BasicInlineQueue}s.
721 ///
722 /// The domains returned by @cpp_func{pw::fuzzer::BasicQueueOf} and
723 /// `Arbitrary<BasicInlineQueue>` do not create FuzzTest containers. This method
724 /// can be used to apply container methods such as `WithMinSize` or
725 /// `UniqueElementsContainerOf` before building a queue from that container.
726 ///
727 /// @param[in] inner  Domain that produces containers.
728 ///
729 /// @retval    Domain that produces `@cpp_class{pw::BasicInlineQueue}`s.
730 template <typename SizeType,
731           size_t kCapacity,
732           int&... ExplicitArgumentBarrier,
733           typename Inner>
734 auto MapToBasicQueue(Inner inner) {
735   using Container = typename Inner::value_type;
736   static_assert(internal::IsContainer<Container>::value);
737   using T = typename Container::value_type;
738   return Map(
739       [](const Container& items) {
740         return BasicInlineQueue<T, SizeType, kCapacity>(items.begin(),
741                                                         items.end());
742       },
743       std::move(inner));
744 }
745 
746 /// Returns a FuzzTest domain that produces @cpp_class{pw::BasicInlineQueue}s.
747 ///
748 /// Use this, @cpp_func{pw::fuzzer::QueueOf}, or
749 /// @cpp_func{pw::fuzzer::ScopedListOf} in place of `fuzztest::ListOf`. The
750 /// queue's maximum size is set by the template parameter.
751 ///
752 /// Alternatively, you can use `Arbitrary<BasicInlineQueue<T, kCapacity>>`.
753 ///
754 /// @param[in] inner  Domain that produces values of type `T`.
755 ///
756 /// @retval    Domain that produces values of type
757 ///            `BasicInlineQueue<T, SizeType, kCapacity>`.
758 template <typename SizeType,
759           size_t kCapacity,
760           int&... ExplicitArgumentBarrier,
761           typename Inner>
762 auto BasicQueueOf(Inner inner) {
763   return MapToBasicQueue<SizeType, kCapacity>(
764       VectorOf<kCapacity>(std::move(inner)));
765 }
766 
767 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
768 /// @cpp_class{pw::BasicInlineQueue}.
769 template <typename T, typename SizeType, size_t kCapacity>
770 struct ArbitraryImpl<BasicInlineQueue<T, SizeType, kCapacity>> {
771   auto operator()() {
772     return BasicQueueOf<SizeType, kCapacity>(Arbitrary<T>());
773   }
774 };
775 
776 /// Implementation of @cpp_func{pw::fuzzer::ContainerOf} for
777 /// @cpp_class{pw::containers::BasicInlineQueue}.
778 ///
779 /// Since inline queues have a static capacity, the returned domains do not
780 /// produce FuzzTest containers, but aggregates. As a result, container methods
781 /// such as `WithMaxSize` cannot be applied. Instead, use
782 /// @cpp_func{pw::fuzzer::MapToQueue} or @cpp_func{pw::fuzzer::MapToBasicQueue}
783 /// to apply constraints to the set of keys and values.
784 ///
785 /// @param[in]  inner   Domain the produces values of type `T`.
786 ///
787 /// @retval     Domain that produces `@cpp_class{pw::BasicInlineQueue}`s.
788 template <typename T, typename SizeType, size_t kCapacity>
789 struct ContainerOfImpl<BasicInlineQueue<T, SizeType, kCapacity>> {
790   template <int&... ExplicitArgumentBarrier, typename Inner>
791   auto operator()(Inner inner) {
792     return BasicQueueOf<SizeType, kCapacity>(std::move(inner));
793   }
794 };
795 
796 /// Transforms a domain that produces containers into a domain that produces
797 /// @cpp_class{pw::InlineQueue}s.
798 ///
799 /// The domains returned by @cpp_func{pw::fuzzer::QueueOf} and
800 /// `Arbitrary<InlineQueue>` do not create FuzzTest containers. This method
801 /// can be used to apply container methods such as `WithMinSize` or
802 /// `UniqueElementsContainerOf` before building a queue from that container.
803 ///
804 /// @param[in] inner  Domain that produces containers.
805 ///
806 /// @retval    Domain that produces `@cpp_class{pw::InlineQueue}`s.
807 template <size_t kCapacity, int&... ExplicitArgumentBarrier, typename Inner>
808 auto MapToQueue(Inner inner) {
809   return MapToBasicQueue<uint16_t, kCapacity>(std::move(inner));
810 }
811 
812 /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineQueue}s.
813 ///
814 /// Use this, @cpp_func{pw::fuzzer::BasicQueueOf}, or
815 /// @cpp_func{pw::fuzzer::ScopedListOf} in place of `fuzztest::ListOf`. The
816 /// queue's maximum size is set by the template parameter.
817 ///
818 /// Alternatively, you can use `Arbitrary<InlineQueue<T, kCapacity>>`.
819 ///
820 /// @param[in] inner  Domain that produces values of type `T`.
821 ///
822 /// @retval    Domain that produces values of type `InlineQueue<T, kCapacity>`.
823 template <size_t kCapacity, int&... ExplicitArgumentBarrier, typename Inner>
824 auto QueueOf(Inner inner) {
825   return BasicQueueOf<uint16_t, kCapacity>(std::move(inner));
826 }
827 
828 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
829 /// @cpp_class{pw::InlineQueue}.
830 template <typename T, size_t kCapacity>
831 struct ArbitraryImpl<InlineQueue<T, kCapacity>> {
832   auto operator()() { return QueueOf<kCapacity>(Arbitrary<T>()); }
833 };
834 
835 /// Associates an `IntrusiveList<T>` with a `Vector<T>` that stores its `Item`s.
836 ///
837 /// The `Item`s are constructed from a sequence of argument tuples passed to
838 // constructor.
839 template <typename T, size_t kMaxSize>
840 class ScopedList {
841  public:
842   ~ScopedList() { list_.clear(); }
843 
844   template <int&... ExplicitArgumentBarrier, typename Tuple>
845   explicit ScopedList(const Vector<Tuple>& arg_tuples) {
846     for (const auto& arg_tuple : arg_tuples) {
847       items_.emplace_back(std::make_from_tuple<T>(arg_tuple));
848       list_.push_back(items_.back());
849     }
850   }
851 
852   ScopedList(const ScopedList& other) = delete;
853   ScopedList& operator=(const ScopedList& other) = delete;
854 
855   ScopedList(ScopedList&& other) { *this = std::move(other); }
856   ScopedList& operator=(ScopedList&& other) {
857     list_.clear();
858     other.list_.clear();
859     items_ = std::move(other.items_);
860     list_.assign(items_.begin(), items_.end());
861     return *this;
862   }
863 
864   const IntrusiveList<T>& list() const { return list_; }
865 
866  private:
867   IntrusiveList<T> list_;
868   Vector<T, kMaxSize> items_;
869 };
870 
871 /// Transforms a domain that produces containers into a domain that produces
872 /// @cpp_class{pw::fuzzer::ScopedList}s.
873 ///
874 /// The domains returned by @cpp_func{pw::fuzzer::ScopedListOf} do not create
875 /// FuzzTest containers. This method can be used to apply container methods such
876 /// as `WithMinSize` or `UniqueElementsContainerOf` before building an intrusive
877 /// list from that container.
878 ///
879 /// @param[in] inner  Domain that produces containers.
880 ///
881 /// @retval    Domain that produces `@cpp_class{pw::fuzzer::ScopedList}`s.
882 template <typename T,
883           size_t kMaxSize,
884           int&... ExplicitArgumentBarrier,
885           typename Inner>
886 auto MapToScopedList(Inner inner) {
887   using Container = typename Inner::value_type;
888   static_assert(internal::IsContainer<Container>::value);
889   using Tuple = typename Container::value_type;
890   static_assert(
891       std::is_same_v<T, decltype(std::make_from_tuple<T>(Tuple()))>,
892       "The domain passed to `pw::fuzzer::MapToScopedList<T, kMaxSize>>` must "
893       "produce `std::tuple`s of constructor arguments for `T`, e.g. using "
894       "`pw::fuzzer::TupleOf`.");
895   return ConstructorOf<ScopedList<T, kMaxSize>>(std::move(inner));
896 }
897 
898 /// Returns a FuzzTest domain that produces @cpp_class{pw::fuzzer::ScopedList}s.
899 ///
900 /// Use this, @cpp_func{pw::fuzzer::BasicQueueOf}, or
901 /// @cpp_func{pw::fuzzer::QueueOf} in place of `fuzztest::ListOf`. The list's
902 /// maximum size is set by the template parameter.
903 ///
904 /// @param[in] inner...  Domains that produces `IntrusiveList<T>::Item`s.
905 ///
906 /// @retval    Domain that produces `ScopedList<T, kMaxSize>`s.
907 template <typename T,
908           size_t kMaxSize,
909           int&... ExplicitArgumentBarrier,
910           typename... Inner>
911 auto ScopedListOf(Inner... inner) {
912   return MapToScopedList<T, kMaxSize>(
913       VectorOf<kMaxSize>(TupleOf(std::move(inner)...)));
914 }
915 
916 ////////////////////////////////////////////////////////////////
917 // pw_string-related types
918 
919 /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineBasicString}s.
920 ///
921 /// Use this in place of `fuzztest::StringOf`. The characters of the string
922 /// are drawn from the given domain. The string capacity is given by the
923 /// template parameter.
924 ///
925 /// Alternatively, you can use `Arbitrary<InlineString<kCapacity>>`.
926 ///
927 /// @param[in] inner  Domain that produces values of a character type.
928 ///
929 /// @retval    Domain that produces `InlineBasicString<kCapacity>`s.
930 template <size_t kCapacity, int&... ExplicitArgumentBarrier, typename Inner>
931 auto StringOf(Inner inner) {
932   return ContainerOf<InlineBasicString<typename Inner::value_type, kCapacity>>(
933              inner)
934       .WithMaxSize(kCapacity);
935 }
936 
937 /// Implementation of @cpp_func{pw::fuzzer::Arbitrary} for
938 /// @cpp_class{pw::InlineBasicString}.
939 template <typename T, size_t kCapacity>
940 struct ArbitraryImpl<InlineBasicString<T, kCapacity>> {
941   auto operator()() { return StringOf<kCapacity>(Arbitrary<T>()); }
942 };
943 
944 /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineString}s.
945 ///
946 /// Use this in place of `fuzztest::String`. The string capacity is given by the
947 /// template parameter.
948 ///
949 /// @retval    Domain that produces `InlineString<kCapacity>`s.
950 template <size_t kCapacity>
951 auto String() {
952   return StringOf<kCapacity>(Arbitrary<char>());
953 }
954 
955 /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineString}s
956 /// containing only ASCII characters.
957 ///
958 /// Use this in place of `fuzztest::AsciiString`. The string capacity is given
959 /// by the template parameter.
960 ///
961 /// @retval    Domain that produces `InlineString<kCapacity>`s.
962 template <size_t kCapacity>
963 auto AsciiString() {
964   return StringOf<kCapacity>(AsciiChar());
965 }
966 
967 /// Returns a FuzzTest domain that produces @cpp_class{pw::InlineString}s
968 /// containing only printable ASCII characters.
969 ///
970 /// Use this in place of `fuzztest::PrintableAsciiString`. The string capacity
971 /// is given by the template parameter.
972 ///
973 /// @retval    Domain that produces printable `InlineString<kCapacity>`s.
974 template <size_t kCapacity>
975 auto PrintableAsciiString() {
976   return StringOf<kCapacity>(PrintableAsciiChar());
977 }
978 
979 }  // namespace pw::fuzzer
980