xref: /aosp_15_r20/external/pigweed/pw_fuzzer/private_overrides/pw_fuzzer/internal/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
17 /// Stubs for the Pigweed-compatible subset of the FuzzTest interface
18 ///
19 /// @rst
20 /// This header provides stubs for the portion of the FuzzTest interface that
21 /// only depends on permitted C++ standard library `headers`_, including
22 /// `macros`_ and `domains`_.
23 ///
24 /// This header is included when FuzzTest is disabled, e.g. for GN, when
25 /// ``dir_pw_third_party_fuzztest`` or ``pw_toolchain_FUZZING_ENABLED`` are not
26 /// set. Otherwise, ``//pw_fuzzer/public/pw_fuzzer/internal/fuzztest.h`` is used
27 /// instead.
28 ///
29 /// .. _domains:
30 /// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md
31 /// .. _headers: https://pigweed.dev/docs/style_guide.html#permitted-headers
32 /// .. _macros:
33 /// https://github.com/google/fuzztest/blob/main/doc/fuzz-test-macro.md
34 /// @endrst
35 
36 #include <array>
37 #include <cmath>
38 #include <initializer_list>
39 #include <limits>
40 #include <optional>
41 #include <string_view>
42 #include <tuple>
43 #include <type_traits>
44 #include <utility>
45 #include <variant>
46 
47 #define FUZZ_TEST(test_suite_name, test_name)                              \
48   TEST(test_suite_name, DISABLED_##test_name) {}                           \
49   auto _pw_fuzzer_##test_suite_name##_##test_name##_FUZZTEST_NOT_PRESENT = \
50       fuzztest::internal::TypeCheckFuzzTest(test_name).IgnoreFunction()
51 
52 #define FUZZ_TEST_F(test_fixture, test_name)                            \
53   TEST_F(test_fixture, DISABLED_##test_name) {}                         \
54   auto _pw_fuzzer_##test_fixture##_##test_name##_FUZZTEST_NOT_PRESENT = \
55       fuzztest::internal::TypeCheckFuzzTest(test_name).IgnoreFunction()
56 
57 namespace fuzztest {
58 
59 /// Stub for a FuzzTest domain that produces values.
60 ///
61 /// In FuzzTest, domains are used to provide values of specific types when
62 /// fuzzing. However, FuzzTest is only optionally supported on host with Clang.
63 /// For other build configurations, this struct provides a FuzzTest-compatible
64 /// stub that can be used to perform limited type-checking at build time.
65 ///
66 /// Fuzzer authors must not invoke this type directly. Instead, use the factory
67 /// methods for domains such as `Arbitrary`, `VectorOf`, `Map`, etc.
68 template <typename T>
69 struct Domain {
70   using value_type = T;
71 };
72 
73 namespace internal {
74 
75 /// Stub for a FuzzTest domain that produces containers of values.
76 ///
77 /// This struct is an extension of `Domain` that add stubs for the methods that
78 /// control container size.
79 template <typename T>
80 struct ContainerDomain : public Domain<T> {
81   template <typename U, typename = std::enable_if_t<std::is_integral_v<U>>>
WithSizeContainerDomain82   ContainerDomain<T>& WithSize(U) {
83     return *this;
84   }
85 
86   template <typename U, typename = std::enable_if_t<std::is_integral_v<U>>>
WithMinSizeContainerDomain87   ContainerDomain<T>& WithMinSize(U) {
88     return *this;
89   }
90 
91   template <typename U, typename = std::enable_if_t<std::is_integral_v<U>>>
WithMaxSizeContainerDomain92   ContainerDomain<T>& WithMaxSize(U) {
93     return *this;
94   }
95 };
96 
97 /// Stub for a FuzzTest domain that produces optional values.
98 ///
99 /// This struct is an extension of `Domain` that add stubs for the methods that
100 /// control nullability.
101 template <typename T>
102 struct OptionalDomain : public Domain<T> {
SetAlwaysNullOptionalDomain103   OptionalDomain<T>& SetAlwaysNull() { return *this; }
SetWithoutNullOptionalDomain104   OptionalDomain<T>& SetWithoutNull() { return *this; }
105 };
106 
107 /// Register a FuzzTest stub.
108 ///
109 /// FuzzTest is only optionally supported on host with Clang. For other build
110 /// configurations, this struct provides a FuzzTest-compatible stub of a test
111 /// registration that only performs limited type-checking at build time.
112 ///
113 /// Fuzzer authors must not invoke this type directly. Instead, use the
114 /// `FUZZ_TEST` and/or `FUZZ_TEST_F` macros.
115 template <typename TargetFunction>
116 struct TypeCheckFuzzTest {
TypeCheckFuzzTestTypeCheckFuzzTest117   TypeCheckFuzzTest(TargetFunction) {}
118 
IgnoreFunctionTypeCheckFuzzTest119   TypeCheckFuzzTest<TargetFunction>& IgnoreFunction() { return *this; }
120 
121   template <int&... ExplicitArgumentBarrier,
122             typename... Domains,
123             typename T = std::decay_t<
124                 std::invoke_result_t<TargetFunction,
125                                      const typename Domains::value_type&...>>>
WithDomainsTypeCheckFuzzTest126   TypeCheckFuzzTest<TargetFunction>& WithDomains(Domains...) {
127     return *this;
128   }
129 
130   template <int&... ExplicitArgumentBarrier,
131             typename... Seeds,
132             typename T = std::decay_t<
133                 std::invoke_result_t<TargetFunction, const Seeds&...>>>
WithSeedsTypeCheckFuzzTest134   TypeCheckFuzzTest<TargetFunction>& WithSeeds(Seeds...) {
135     return *this;
136   }
137 };
138 
139 }  // namespace internal
140 
141 // The remaining functions match those defined by fuzztest/fuzztest.h.
142 //
143 // This namespace is here only as a way to disable ADL (argument-dependent
144 // lookup). Names should be used from the fuzztest:: namespace.
145 namespace internal_no_adl {
146 
147 ////////////////////////////////////////////////////////////////
148 // Arbitrary domains
149 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#arbitrary-domains
150 
151 template <typename T>
Arbitrary()152 auto Arbitrary() {
153   return Domain<T>{};
154 }
155 
156 ////////////////////////////////////////////////////////////////
157 // Other miscellaneous domains
158 // These typically appear later in docs and tests. They are placed early in this
159 // file to allow other domains to be defined using them.
160 
161 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#oneof
162 template <int&... ExplicitArgumentBarrier, typename T, typename... Domains>
OneOf(Domain<T>,Domains...)163 auto OneOf(Domain<T>, Domains...) {
164   return Domain<T>{};
165 }
166 
167 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#oneof
168 template <typename T>
Just(T)169 auto Just(T) {
170   return Domain<T>{};
171 }
172 
173 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#map
174 template <int&... ExplicitArgumentBarrier, typename Mapper, typename... Inner>
Map(Mapper,Inner...)175 auto Map(Mapper, Inner...) {
176   return Domain<std::decay_t<
177       std::invoke_result_t<Mapper, typename Inner::value_type&...>>>{};
178 }
179 
180 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#flatmap
181 template <typename FlatMapper, typename... Inner>
182 using FlatMapOutputDomain = std::decay_t<
183     std::invoke_result_t<FlatMapper, typename Inner::value_type&...>>;
184 template <int&... ExplicitArgumentBarrier,
185           typename FlatMapper,
186           typename... Inner>
FlatMap(FlatMapper,Inner...)187 auto FlatMap(FlatMapper, Inner...) {
188   return Domain<
189       typename FlatMapOutputDomain<FlatMapper, Inner...>::value_type>{};
190 }
191 
192 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#filter
193 template <int&... ExplicitArgumentBarrier, typename T, typename Pred>
Filter(Pred,Domain<T>)194 auto Filter(Pred, Domain<T>) {
195   return Domain<T>{};
196 }
197 
198 ////////////////////////////////////////////////////////////////
199 // Numerical domains
200 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#numerical-domains
201 
202 template <typename T>
InRange(T min,T max)203 auto InRange(T min, T max) {
204   return Filter([min, max](T t) { return min <= t && t <= max; },
205                 Arbitrary<T>());
206 }
207 
208 template <typename T>
NonZero()209 auto NonZero() {
210   return Filter([](T t) { return t != 0; }, Arbitrary<T>());
211 }
212 
213 template <typename T>
Positive()214 auto Positive() {
215   if constexpr (std::is_floating_point_v<T>) {
216     return InRange<T>(std::numeric_limits<T>::denorm_min(),
217                       std::numeric_limits<T>::max());
218   } else {
219     return InRange<T>(T{1}, std::numeric_limits<T>::max());
220   }
221 }
222 
223 template <typename T>
NonNegative()224 auto NonNegative() {
225   return InRange<T>(T{}, std::numeric_limits<T>::max());
226 }
227 
228 template <typename T>
Negative()229 auto Negative() {
230   static_assert(!std::is_unsigned_v<T>,
231                 "Negative<T>() can only be used with with signed T-s! "
232                 "For char, consider using signed char.");
233   if constexpr (std::is_floating_point_v<T>) {
234     return InRange<T>(std::numeric_limits<T>::lowest(),
235                       -std::numeric_limits<T>::denorm_min());
236   } else {
237     return InRange<T>(std::numeric_limits<T>::min(), T{-1});
238   }
239 }
240 
241 template <typename T>
NonPositive()242 auto NonPositive() {
243   static_assert(!std::is_unsigned_v<T>,
244                 "NonPositive<T>() can only be used with with signed T-s! "
245                 "For char, consider using signed char.");
246   return InRange<T>(std::numeric_limits<T>::lowest(), T{});
247 }
248 
249 template <typename T>
Finite()250 auto Finite() {
251   static_assert(std::is_floating_point_v<T>,
252                 "Finite<T>() can only be used with floating point types!");
253   return Filter([](T f) { return std::isfinite(f); }, Arbitrary<T>());
254 }
255 
256 ////////////////////////////////////////////////////////////////
257 // Character domains
258 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#character-domains
259 
NonZeroChar()260 inline auto NonZeroChar() { return Positive<char>(); }
NumericChar()261 inline auto NumericChar() { return InRange<char>('0', '9'); }
LowerChar()262 inline auto LowerChar() { return InRange<char>('a', 'z'); }
UpperChar()263 inline auto UpperChar() { return InRange<char>('A', 'Z'); }
AlphaChar()264 inline auto AlphaChar() { return OneOf(LowerChar(), UpperChar()); }
AlphaNumericChar()265 inline auto AlphaNumericChar() { return OneOf(AlphaChar(), NumericChar()); }
AsciiChar()266 inline auto AsciiChar() { return InRange<char>(0, 127); }
PrintableAsciiChar()267 inline auto PrintableAsciiChar() { return InRange<char>(32, 126); }
268 
269 ////////////////////////////////////////////////////////////////
270 // Regular expression domains
271 
272 // TODO: b/285775246 - Add support for `fuzztest::InRegexp`.
273 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#inregexp-domains
274 // inline auto InRegexp(std::string_view) {
275 //   return Domain<std::string_view>{};
276 // }
277 
278 ////////////////////////////////////////////////////////////////
279 // Enumerated domains
280 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#elementof-domains
281 
282 template <typename T>
ElementOf(std::initializer_list<T>)283 auto ElementOf(std::initializer_list<T>) {
284   return Domain<T>{};
285 }
286 
287 template <typename T>
BitFlagCombinationOf(std::initializer_list<T>)288 auto BitFlagCombinationOf(std::initializer_list<T>) {
289   return Domain<T>{};
290 }
291 
292 ////////////////////////////////////////////////////////////////
293 // Container domains
294 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators
295 
296 template <typename T, int&... ExplicitArgumentBarrier, typename U>
ContainerOf(Domain<U>)297 auto ContainerOf(Domain<U>) {
298   return internal::ContainerDomain<T>{};
299 }
300 
301 template <template <typename, typename...> class T,
302           int&... ExplicitArgumentBarrier,
303           typename U>
ContainerOf(Domain<U>)304 auto ContainerOf(Domain<U>) {
305   return internal::ContainerDomain<T<U>>{};
306 }
307 
308 template <typename T, int&... ExplicitArgumentBarrier, typename U>
UniqueElementsContainerOf(Domain<U>)309 auto UniqueElementsContainerOf(Domain<U>) {
310   return internal::ContainerDomain<T>{};
311 }
312 
313 template <int&... ExplicitArgumentBarrier, typename T>
NonEmpty(internal::ContainerDomain<T> inner)314 auto NonEmpty(internal::ContainerDomain<T> inner) {
315   return inner.WithMinSize(1);
316 }
317 
318 ////////////////////////////////////////////////////////////////
319 // Aggregate domains
320 // https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators
321 
322 template <int&... ExplicitArgumentBarrier, typename T, typename... Domains>
ArrayOf(Domain<T>,Domains...others)323 auto ArrayOf(Domain<T>, Domains... others) {
324   return Domain<std::array<T, 1 + sizeof...(others)>>{};
325 }
326 
327 template <int N, int&... ExplicitArgumentBarrier, typename T>
ArrayOf(const Domain<T> &)328 auto ArrayOf(const Domain<T>&) {
329   return Domain<std::array<T, N>>{};
330 }
331 
332 template <typename T, int&... ExplicitArgumentBarrier, typename... Inner>
StructOf(Inner...)333 auto StructOf(Inner...) {
334   return Domain<T>{};
335 }
336 
337 template <typename T, int&... ExplicitArgumentBarrier>
ConstructorOf()338 auto ConstructorOf() {
339   return Domain<T>{};
340 }
341 
342 template <typename T,
343           int&... ExplicitArgumentBarrier,
344           typename U,
345           typename... Inner>
ConstructorOf(Domain<U>,Inner...inner)346 auto ConstructorOf(Domain<U>, Inner... inner) {
347   return ConstructorOf<T>(inner...);
348 }
349 
350 template <int&... ExplicitArgumentBarrier, typename T1, typename T2>
PairOf(Domain<T1>,Domain<T2>)351 auto PairOf(Domain<T1>, Domain<T2>) {
352   return Domain<std::pair<T1, T2>>{};
353 }
354 
355 template <int&... ExplicitArgumentBarrier, typename... Inner>
TupleOf(Inner...)356 auto TupleOf(Inner...) {
357   return Domain<std::tuple<typename Inner::value_type...>>{};
358 }
359 
360 template <typename T, int&... ExplicitArgumentBarrier, typename... Inner>
VariantOf(Inner...)361 auto VariantOf(Inner...) {
362   return Domain<T>{};
363 }
364 
365 template <int&... ExplicitArgumentBarrier, typename... Inner>
VariantOf(Inner...)366 auto VariantOf(Inner...) {
367   return Domain<std::variant<typename Inner::value_type...>>{};
368 }
369 
370 template <template <typename> class Optional,
371           int&... ExplicitArgumentBarrier,
372           typename T>
OptionalOf(Domain<T>)373 auto OptionalOf(Domain<T>) {
374   return internal::OptionalDomain<Optional<T>>{};
375 }
376 
377 template <int&... ExplicitArgumentBarrier, typename T>
OptionalOf(Domain<T> inner)378 auto OptionalOf(Domain<T> inner) {
379   return OptionalOf<std::optional>(inner);
380 }
381 
382 template <typename T>
NullOpt()383 auto NullOpt() {
384   return internal::OptionalDomain<std::optional<T>>{}.SetAlwaysNull();
385 }
386 
387 template <int&... ExplicitArgumentBarrier, typename T>
NonNull(internal::OptionalDomain<T> inner)388 auto NonNull(internal::OptionalDomain<T> inner) {
389   return inner.SetWithoutNull();
390 }
391 
392 }  // namespace internal_no_adl
393 
394 // Inject the names from internal_no_adl into fuzztest, without allowing for
395 // ADL. Note that an `inline` namespace would not have this effect (ie it would
396 // still allow ADL to trigger).
397 using namespace internal_no_adl;  // NOLINT
398 
399 }  // namespace fuzztest
400