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