1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef BASE_TEST_FUZZTEST_SUPPORT_H_
6 #define BASE_TEST_FUZZTEST_SUPPORT_H_
7
8 #include <optional>
9 #include <string>
10 #include <tuple>
11 #include <vector>
12
13 #include "base/containers/to_vector.h"
14 #include "base/types/optional_ref.h"
15 #include "base/values.h"
16 #include "third_party/fuzztest/src/fuzztest/fuzztest.h"
17
18 namespace {
19
20 template <typename T>
21 requires std::copy_constructible<T>
Wrap(base::optional_ref<const T> maybe_value)22 std::optional<std::tuple<T>> Wrap(base::optional_ref<const T> maybe_value) {
23 return maybe_value.has_value() ? std::optional(std::tuple<T>{*maybe_value})
24 : std::nullopt;
25 }
26
ArbitraryValueNull()27 auto ArbitraryValueNull() {
28 return fuzztest::ReversibleMap(
29 [] { return base::Value(); },
30 [](const base::Value& value) { return std::optional<std::tuple<>>{}; });
31 }
32
ArbitraryValueBool()33 auto ArbitraryValueBool() {
34 return fuzztest::ReversibleMap(
35 [](bool boolean) { return base::Value(boolean); },
36 [](const base::Value& value) { return Wrap<bool>(value.GetIfBool()); },
37 fuzztest::Arbitrary<bool>());
38 }
39
ArbitraryValueInt()40 auto ArbitraryValueInt() {
41 return fuzztest::ReversibleMap(
42 [](int number) { return base::Value(number); },
43 [](const base::Value& value) { return Wrap<int>(value.GetIfInt()); },
44 fuzztest::Arbitrary<int>());
45 }
46
ArbitraryValueDouble()47 auto ArbitraryValueDouble() {
48 return fuzztest::ReversibleMap(
49 [](double number) { return base::Value(number); },
50 [](const base::Value& value) {
51 return Wrap<double>(value.GetIfDouble());
52 },
53 fuzztest::Finite<double>());
54 }
55
ArbitraryValueString()56 auto ArbitraryValueString() {
57 return fuzztest::ReversibleMap(
58 [](std::string string) { return base::Value(string); },
59 [](const base::Value& value) {
60 return Wrap<std::string>(value.GetIfString());
61 },
62 fuzztest::String());
63 }
64
ArbitraryValueBlob()65 auto ArbitraryValueBlob() {
66 return fuzztest::ReversibleMap(
67 [](std::vector<uint8_t> blob) { return base::Value(blob); },
68 [](const base::Value& value) {
69 return Wrap<std::vector<uint8_t>>(value.GetIfBlob());
70 },
71 fuzztest::Arbitrary<std::vector<uint8_t>>());
72 }
73
ArbitraryValueList(fuzztest::Domain<base::Value> entry_domain)74 auto ArbitraryValueList(fuzztest::Domain<base::Value> entry_domain) {
75 return fuzztest::ReversibleMap(
76 [](std::vector<base::Value> values) {
77 base::Value::List list;
78 for (auto& value : values) {
79 list.Append(std::move(value));
80 }
81 return base::Value(std::move(list));
82 },
83 [](const base::Value& value) {
84 auto maybe_list = base::optional_ref(value.GetIfList());
85 return maybe_list.has_value()
86 ? std::make_optional(std::tuple{
87 base::ToVector(*maybe_list, &base::Value::Clone)})
88 : std::nullopt;
89 },
90 fuzztest::ContainerOf<std::vector<base::Value>>(entry_domain));
91 }
92
ArbitraryValueDict(fuzztest::Domain<base::Value> value_domain)93 auto ArbitraryValueDict(fuzztest::Domain<base::Value> value_domain) {
94 return fuzztest::ReversibleMap(
95 [](std::vector<std::pair<std::string, base::Value>> e) {
96 return base::Value(base::Value::Dict(std::make_move_iterator(e.begin()),
97 std::make_move_iterator(e.end())));
98 },
99 [](const base::Value& value) {
100 auto maybe_dict = base::optional_ref(value.GetIfDict());
101 return maybe_dict.has_value()
102 ? std::make_optional(std::tuple{base::ToVector(
103 *maybe_dict,
104 [](const auto& entry) {
105 return std::make_pair(entry.first,
106 entry.second.Clone());
107 })})
108 : std::nullopt;
109 },
110 fuzztest::ContainerOf<std::vector<std::pair<std::string, base::Value>>>(
111 fuzztest::PairOf(fuzztest::String(), value_domain)));
112 }
113
ArbitraryValue()114 fuzztest::Domain<base::Value> ArbitraryValue() {
115 fuzztest::DomainBuilder builder;
116 builder.Set<base::Value>(
117 "value",
118 fuzztest::OneOf(ArbitraryValueNull(), ArbitraryValueBool(),
119 ArbitraryValueInt(), ArbitraryValueDouble(),
120 ArbitraryValueString(), ArbitraryValueBlob(),
121 ArbitraryValueList(builder.Get<base::Value>("value")),
122 ArbitraryValueDict(builder.Get<base::Value>("value"))));
123 return std::move(builder).Finalize<base::Value>("value");
124 }
125
126 } // namespace
127
128 template <>
129 class fuzztest::internal::ArbitraryImpl<base::Value>
130 : public fuzztest::Domain<base::Value> {
131 public:
ArbitraryImpl()132 ArbitraryImpl() : fuzztest::Domain<base::Value>(ArbitraryValue()) {}
133 };
134
135 template <>
136 class fuzztest::internal::ArbitraryImpl<base::Value::Dict>
137 : public fuzztest::internal::ReversibleMapImpl<
138 base::Value::Dict (*)(
139 std::vector<std::pair<std::string, base::Value>>),
140 std::optional<
141 std::tuple<std::vector<std::pair<std::string, base::Value>>>> (*)(
142 const base::Value::Dict&),
143 fuzztest::internal::ArbitraryImpl<
144 std::vector<std::pair<std::string, base::Value>>>> {
145 public:
ArbitraryImpl()146 ArbitraryImpl()
147 : fuzztest::internal::ReversibleMapImpl<
148 base::Value::Dict (*)(
149 std::vector<std::pair<std::string, base::Value>>),
150 std::optional<std::tuple<
151 std::vector<std::pair<std::string, base::Value>>>> (*)(
152 const base::Value::Dict&),
153 fuzztest::internal::ArbitraryImpl<
154 std::vector<std::pair<std::string, base::Value>>>>(
155 [](std::vector<std::pair<std::string, base::Value>> e) {
156 return base::Value::Dict(std::make_move_iterator(e.begin()),
157 std::make_move_iterator(e.end()));
158 },
159 [](const base::Value::Dict& value) {
160 return std::make_optional(
161 std::tuple{base::ToVector(value, [](const auto& entry) {
162 return std::make_pair(entry.first, entry.second.Clone());
163 })});
164 },
165 fuzztest::internal::ArbitraryImpl<
166 std::vector<std::pair<std::string, base::Value>>>()) {}
167 };
168
169 #endif // BASE_TEST_FUZZTEST_SUPPORT_H_
170