blob: ab0f70d9857d143f2505fbef9c5a021c221b359e [file]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TEST_FUZZTEST_SUPPORT_H_
#define BASE_TEST_FUZZTEST_SUPPORT_H_
#include <optional>
#include <string>
#include <tuple>
#include <vector>
#include "base/containers/to_vector.h"
#include "base/types/optional_ref.h"
#include "base/values.h"
#include "third_party/fuzztest/src/fuzztest/fuzztest.h"
namespace {
template <typename T>
requires std::copy_constructible<T>
std::optional<std::tuple<T>> Wrap(base::optional_ref<const T> maybe_value) {
return maybe_value.has_value() ? std::optional(std::tuple<T>{*maybe_value})
: std::nullopt;
}
auto ArbitraryValueNull() {
return fuzztest::ReversibleMap(
[] { return base::Value(); },
[](const base::Value& value) { return std::optional<std::tuple<>>{}; });
}
auto ArbitraryValueBool() {
return fuzztest::ReversibleMap(
[](bool boolean) { return base::Value(boolean); },
[](const base::Value& value) { return Wrap<bool>(value.GetIfBool()); },
fuzztest::Arbitrary<bool>());
}
auto ArbitraryValueInt() {
return fuzztest::ReversibleMap(
[](int number) { return base::Value(number); },
[](const base::Value& value) { return Wrap<int>(value.GetIfInt()); },
fuzztest::Arbitrary<int>());
}
auto ArbitraryValueDouble() {
return fuzztest::ReversibleMap(
[](double number) { return base::Value(number); },
[](const base::Value& value) {
return Wrap<double>(value.GetIfDouble());
},
fuzztest::Finite<double>());
}
auto ArbitraryValueString() {
return fuzztest::ReversibleMap(
[](std::string string) { return base::Value(std::move(string)); },
[](const base::Value& value) {
return Wrap<std::string>(value.GetIfString());
},
fuzztest::Utf8String());
}
auto ArbitraryValueBlob() {
return fuzztest::ReversibleMap(
[](std::vector<uint8_t> blob) { return base::Value(std::move(blob)); },
[](const base::Value& value) {
return Wrap<std::vector<uint8_t>>(value.GetIfBlob());
},
fuzztest::Arbitrary<std::vector<uint8_t>>());
}
auto ArbitraryList(fuzztest::Domain<base::Value> entry_domain) {
return fuzztest::ReversibleMap(
[](std::vector<base::Value> values) {
auto list = base::ListValue::with_capacity(values.size());
for (auto& value : values) {
list.Append(std::move(value));
}
return list;
},
[](const base::ListValue& list) {
return std::optional{
std::tuple{base::ToVector(list, &base::Value::Clone)}};
},
fuzztest::ContainerOf<std::vector<base::Value>>(entry_domain));
}
auto ArbitraryValueList(fuzztest::Domain<base::Value> entry_domain) {
return fuzztest::ReversibleMap(
[](base::ListValue list) { return base::Value(std::move(list)); },
[](const base::Value& value) {
const auto* list = value.GetIfList();
return list ? std::optional{std::tuple{list->Clone()}} : std::nullopt;
},
ArbitraryList(entry_domain));
}
auto ArbitraryDict(fuzztest::Domain<base::Value> value_domain) {
return fuzztest::ReversibleMap(
[](std::vector<std::pair<std::string, base::Value>> e) {
return base::DictValue(std::make_move_iterator(e.begin()),
std::make_move_iterator(e.end()));
},
[](const base::DictValue& dict) {
return std::optional{
std::tuple{base::ToVector(dict, [](const auto& entry) {
return std::make_pair(entry.first, entry.second.Clone());
})}};
},
fuzztest::ContainerOf<std::vector<std::pair<std::string, base::Value>>>(
fuzztest::PairOf(fuzztest::Utf8String(), value_domain)));
}
auto ArbitraryValueDict(fuzztest::Domain<base::Value> value_domain) {
return fuzztest::ReversibleMap(
[](base::DictValue dict) { return base::Value(std::move(dict)); },
[](const base::Value& value) {
const auto* dict = value.GetIfDict();
return dict ? std::optional{std::tuple{dict->Clone()}} : std::nullopt;
},
ArbitraryDict(value_domain));
}
fuzztest::Domain<base::Value> ArbitraryValue() {
fuzztest::DomainBuilder builder;
builder.Set<base::Value>(
"value",
fuzztest::OneOf(ArbitraryValueNull(), ArbitraryValueBool(),
ArbitraryValueInt(), ArbitraryValueDouble(),
ArbitraryValueString(), ArbitraryValueBlob(),
ArbitraryValueList(builder.Get<base::Value>("value")),
ArbitraryValueDict(builder.Get<base::Value>("value"))));
return std::move(builder).Finalize<base::Value>("value");
}
} // namespace
template <>
class fuzztest::internal::ArbitraryImpl<base::Value>
: public fuzztest::Domain<base::Value> {
public:
ArbitraryImpl() : fuzztest::Domain<base::Value>(ArbitraryValue()) {}
};
template <>
class fuzztest::internal::ArbitraryImpl<base::DictValue>
: public fuzztest::Domain<base::DictValue> {
public:
ArbitraryImpl()
: fuzztest::Domain<base::DictValue>(ArbitraryDict(ArbitraryValue())) {}
};
template <>
class fuzztest::internal::ArbitraryImpl<base::ListValue>
: public fuzztest::Domain<base::ListValue> {
public:
ArbitraryImpl()
: fuzztest::Domain<base::ListValue>(ArbitraryList(ArbitraryValue())) {}
};
#endif // BASE_TEST_FUZZTEST_SUPPORT_H_