// 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 #include #include #include #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 requires std::copy_constructible std::optional> Wrap(base::optional_ref maybe_value) { return maybe_value.has_value() ? std::optional(std::tuple{*maybe_value}) : std::nullopt; } auto ArbitraryValueNull() { return fuzztest::ReversibleMap( [] { return base::Value(); }, [](const base::Value& value) { return std::optional>{}; }); } auto ArbitraryValueBool() { return fuzztest::ReversibleMap( [](bool boolean) { return base::Value(boolean); }, [](const base::Value& value) { return Wrap(value.GetIfBool()); }, fuzztest::Arbitrary()); } auto ArbitraryValueInt() { return fuzztest::ReversibleMap( [](int number) { return base::Value(number); }, [](const base::Value& value) { return Wrap(value.GetIfInt()); }, fuzztest::Arbitrary()); } auto ArbitraryValueDouble() { return fuzztest::ReversibleMap( [](double number) { return base::Value(number); }, [](const base::Value& value) { return Wrap(value.GetIfDouble()); }, fuzztest::Finite()); } auto ArbitraryValueString() { return fuzztest::ReversibleMap( [](std::string string) { return base::Value(string); }, [](const base::Value& value) { return Wrap(value.GetIfString()); }, fuzztest::String()); } auto ArbitraryValueBlob() { return fuzztest::ReversibleMap( [](std::vector blob) { return base::Value(blob); }, [](const base::Value& value) { return Wrap>(value.GetIfBlob()); }, fuzztest::Arbitrary>()); } auto ArbitraryValueList(fuzztest::Domain entry_domain) { return fuzztest::ReversibleMap( [](std::vector values) { base::Value::List list; for (auto& value : values) { list.Append(std::move(value)); } return base::Value(std::move(list)); }, [](const base::Value& value) { auto maybe_list = base::optional_ref(value.GetIfList()); return maybe_list.has_value() ? std::make_optional(std::tuple{ base::ToVector(*maybe_list, &base::Value::Clone)}) : std::nullopt; }, fuzztest::ContainerOf>(entry_domain)); } auto ArbitraryValueDict(fuzztest::Domain value_domain) { return fuzztest::ReversibleMap( [](std::vector> e) { return base::Value(base::Value::Dict(std::make_move_iterator(e.begin()), std::make_move_iterator(e.end()))); }, [](const base::Value& value) { auto maybe_dict = base::optional_ref(value.GetIfDict()); return maybe_dict.has_value() ? std::make_optional(std::tuple{base::ToVector( *maybe_dict, [](const auto& entry) { return std::make_pair(entry.first, entry.second.Clone()); })}) : std::nullopt; }, fuzztest::ContainerOf>>( fuzztest::PairOf(fuzztest::String(), value_domain))); } fuzztest::Domain ArbitraryValue() { fuzztest::DomainBuilder builder; builder.Set( "value", fuzztest::OneOf(ArbitraryValueNull(), ArbitraryValueBool(), ArbitraryValueInt(), ArbitraryValueDouble(), ArbitraryValueString(), ArbitraryValueBlob(), ArbitraryValueList(builder.Get("value")), ArbitraryValueDict(builder.Get("value")))); return std::move(builder).Finalize("value"); } } // namespace template <> class fuzztest::internal::ArbitraryImpl : public fuzztest::Domain { public: ArbitraryImpl() : fuzztest::Domain(ArbitraryValue()) {} }; template <> class fuzztest::internal::ArbitraryImpl : public fuzztest::internal::ReversibleMapImpl< base::Value::Dict (*)( std::vector>), std::optional< std::tuple>>> (*)( const base::Value::Dict&), fuzztest::internal::ArbitraryImpl< std::vector>>> { public: ArbitraryImpl() : fuzztest::internal::ReversibleMapImpl< base::Value::Dict (*)( std::vector>), std::optional>>> (*)( const base::Value::Dict&), fuzztest::internal::ArbitraryImpl< std::vector>>>( [](std::vector> e) { return base::Value::Dict(std::make_move_iterator(e.begin()), std::make_move_iterator(e.end())); }, [](const base::Value::Dict& value) { return std::make_optional( std::tuple{base::ToVector(value, [](const auto& entry) { return std::make_pair(entry.first, entry.second.Clone()); })}); }, fuzztest::internal::ArbitraryImpl< std::vector>>()) {} }; #endif // BASE_TEST_FUZZTEST_SUPPORT_H_