You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
15 KiB
406 lines
15 KiB
// Copyright 2022 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Tests of domain ContainerOf, and various shorthands such as VectorOf.
|
|
|
|
#include <array>
|
|
#include <cstdio>
|
|
#include <initializer_list>
|
|
#include <list>
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include "absl/container/flat_hash_map.h"
|
|
#include "absl/container/flat_hash_set.h"
|
|
#include "absl/random/random.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "./fuzztest/domain_core.h"
|
|
#include "./domain_tests/domain_testing.h"
|
|
#include "./fuzztest/internal/meta.h"
|
|
|
|
namespace fuzztest {
|
|
namespace {
|
|
|
|
using ::testing::_;
|
|
using ::testing::AllOf;
|
|
using ::testing::Contains;
|
|
using ::testing::Each;
|
|
using ::testing::ElementsAre;
|
|
using ::testing::FieldsAre;
|
|
using ::testing::Ge;
|
|
using ::testing::Gt;
|
|
using ::testing::HasSubstr;
|
|
using ::testing::IsEmpty;
|
|
using ::testing::Le;
|
|
using ::testing::MatchesRegex;
|
|
using ::testing::Pair;
|
|
using ::testing::SizeIs;
|
|
|
|
TEST(ContainerCombinatorTest, ValueTypeOfListContainerIsInferred) {
|
|
for (const auto& value :
|
|
GenerateValues(ContainerOf<std::list>(Positive<int>()).WithSize(3))) {
|
|
static_assert(std::is_same_v<decltype(value.user_value), std::list<int>>);
|
|
ASSERT_THAT(value.user_value, SizeIs(3));
|
|
ASSERT_THAT(value.user_value, Each(Ge(1)));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, ValueTypeOfVectorContainerIsInferred) {
|
|
for (const auto& value :
|
|
GenerateValues(ContainerOf<std::vector>(InRange(-6.0F, 6.0F))
|
|
.WithMinSize(2)
|
|
.WithMaxSize(5))) {
|
|
static_assert(
|
|
std::is_same_v<decltype(value.user_value), std::vector<float>>);
|
|
ASSERT_THAT(value.user_value, SizeIs(IsInClosedRange(2, 5)));
|
|
ASSERT_THAT(value.user_value, Each(IsInClosedRange(-6.0, 6.0)));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, ValueTypeOfSetContainerIsInferred) {
|
|
for (const auto& value : GenerateValues(
|
|
ContainerOf<std::set>(NonEmpty(PrintableAsciiString())))) {
|
|
static_assert(
|
|
std::is_same_v<decltype(value.user_value), std::set<std::string>>);
|
|
ASSERT_THAT(
|
|
value.user_value, // Each string in the set
|
|
Each(AllOf(Not(IsEmpty()), // is not empty, and each
|
|
Each( // character in it is
|
|
IsInClosedRange(32, 126))))); // printable ASCII.
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, MinSizeCanBeLargerThanDefaultMaxSize) {
|
|
static constexpr size_t kDefaultMaxSize = 1000;
|
|
auto domain = ContainerOf<std::vector>(Arbitrary<int>())
|
|
.WithMinSize(kDefaultMaxSize + 1);
|
|
absl::BitGen bitgen;
|
|
auto val = Value(domain, bitgen);
|
|
val.Mutate(domain, bitgen, {}, false);
|
|
ASSERT_THAT(val.user_value, SizeIs(Gt(kDefaultMaxSize)));
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, FailsWithInconsistentMinAndMaxSizes) {
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
ContainerOf<std::vector>(Arbitrary<int>())
|
|
.WithMinSize(100)
|
|
.WithMaxSize(99),
|
|
"Maximal size 99 cannot be smaller than minimal size 100");
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
ContainerOf<std::vector>(Arbitrary<int>())
|
|
.WithMaxSize(100)
|
|
.WithMinSize(101),
|
|
"Minimal size 101 cannot be larger than maximal size 100");
|
|
}
|
|
|
|
TEST(SequencedContainerTest, InitGeneratesSeeds) {
|
|
auto domain =
|
|
ContainerOf<std::vector>(Arbitrary<int>()).WithSeeds({{1, 3, 3, 7}});
|
|
|
|
EXPECT_THAT(GenerateInitialValues(domain, 1000),
|
|
Contains(Value(domain, {1, 3, 3, 7})));
|
|
}
|
|
|
|
TEST(AssociativeContainerTest, InitGeneratesSeeds) {
|
|
auto domain = ContainerOf<absl::flat_hash_map<std::string, int>>(
|
|
PairOf(Arbitrary<std::string>(), Arbitrary<int>()))
|
|
.WithSeeds({{{"hello", 7}, {"world", 42}}});
|
|
|
|
EXPECT_THAT(GenerateInitialValues(domain, 1000),
|
|
Contains(Value(domain, {{"hello", 7}, {"world", 42}})));
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, VectorOf) {
|
|
for (const auto& value : GenerateValues(VectorOf(InRange(-5, 5)))) {
|
|
ASSERT_THAT(value.user_value, Each(IsInClosedRange(-5, 5)));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, DequeOf) {
|
|
for (const auto& value : GenerateValues(DequeOf(InRange(-5, 5)))) {
|
|
ASSERT_THAT(value.user_value, Each(IsInClosedRange(-5, 5)));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, ListOf) {
|
|
for (const auto& value : GenerateValues(ListOf(InRange(-5, 5)))) {
|
|
ASSERT_THAT(value.user_value, Each(IsInClosedRange(-5, 5)));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, SetOf) {
|
|
for (const auto& value :
|
|
GenerateValues(SetOf(InRange(-10, 50)).WithMaxSize(5))) {
|
|
static_assert(std::is_same_v<decltype(value.user_value), std::set<int>>);
|
|
ASSERT_THAT(value.user_value, SizeIs(Le(5)));
|
|
ASSERT_THAT(value.user_value, Each(IsInClosedRange(-10, 50)));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, MapOf) {
|
|
auto key_domain = InRange(1, 1000);
|
|
auto value_domain = InRange(-500.0F, 0.0F);
|
|
for (const auto& value :
|
|
GenerateValues(MapOf(std::move(key_domain), std::move(value_domain))
|
|
.WithMaxSize(50))) {
|
|
static_assert(
|
|
std::is_same_v<decltype(value.user_value), std::map<int, float>>);
|
|
ASSERT_THAT(value.user_value, SizeIs(Le(50)));
|
|
ASSERT_THAT(value.user_value, Each(Pair(IsInClosedRange(1, 1000),
|
|
IsInClosedRange(-500.0F, 0.0F))));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, UnorderedSetOf) {
|
|
for (const auto& value :
|
|
GenerateValues(UnorderedSetOf(InRange(100, 1000)).WithMaxSize(5))) {
|
|
static_assert(
|
|
std::is_same_v<decltype(value.user_value), std::unordered_set<int>>);
|
|
ASSERT_THAT(value.user_value, Each(IsInClosedRange(100, 1000)));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, UnorderedMapOf) {
|
|
auto key_domain = InRange(-500.0F, 0.0F);
|
|
auto value_domain = InRange(1, 1000);
|
|
for (const auto& value : GenerateValues(
|
|
UnorderedMapOf(std::move(key_domain), std::move(value_domain))
|
|
.WithMinSize(10)
|
|
.WithMaxSize(50))) {
|
|
static_assert(std::is_same_v<decltype(value.user_value),
|
|
std::unordered_map<float, int>>);
|
|
ASSERT_THAT(value.user_value, SizeIs(IsInClosedRange(10, 50)));
|
|
ASSERT_THAT(value.user_value, Each(Pair(IsInClosedRange(-500.0F, 0.0F),
|
|
IsInClosedRange(1, 1000))));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, FlatHashMap) {
|
|
auto key_domain = InRange(-500.0F, 0.0F);
|
|
auto value_domain = InRange(1, 1000);
|
|
for (const auto& value : GenerateValues(
|
|
ContainerOf<absl::flat_hash_map<float, int>>(
|
|
PairOf(std::move(key_domain), std::move(value_domain)))
|
|
.WithMinSize(10)
|
|
.WithMaxSize(50))) {
|
|
static_assert(std::is_same<decltype(value.user_value),
|
|
absl::flat_hash_map<float, int>>::value);
|
|
ASSERT_THAT(value.user_value, Each(Pair(IsInClosedRange(-500.0F, 0.0F),
|
|
IsInClosedRange(1, 1000))));
|
|
}
|
|
}
|
|
|
|
MATCHER(ElementsAreUnique, absl::StrCat(negation ? "has duplicate elements"
|
|
: "has unique elements")) {
|
|
// Note that we avoid using testing::IsSubsetOf(some_values) here because it
|
|
// isn't optimized for some_values being an associative collection of values.
|
|
using ArgT = std::remove_reference_t<decltype(arg)>;
|
|
absl::flat_hash_set<internal::value_type_t<ArgT>> copy(arg.begin(),
|
|
arg.end());
|
|
return arg.size() == copy.size();
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, UniqueElementsContainerOf) {
|
|
// An std::unordered_multiset<T> can contain multiple elements with the same
|
|
// value, but let's say our testing would benefit from limiting it to a single
|
|
// element with each value; we could use UniqueElementsContainerOf to produce
|
|
// just such a domain of std::unordered_multiset<T> values.
|
|
for (const auto& value :
|
|
GenerateValues(UniqueElementsContainerOf<std::unordered_multiset<int>>(
|
|
InRange(1, 100))
|
|
.WithSize(7))) {
|
|
static_assert(std::is_same_v<decltype(value.user_value),
|
|
std::unordered_multiset<int>>);
|
|
ASSERT_THAT(value.user_value, SizeIs(7));
|
|
ASSERT_THAT(value.user_value, Each(IsInClosedRange(1, 100)));
|
|
}
|
|
}
|
|
|
|
TEST(UniqueElementsContainerTest, InitGeneratesSeeds) {
|
|
auto domain =
|
|
UniqueElementsContainerOf<std::unordered_multiset<int>>(Arbitrary<int>())
|
|
.WithSeeds({{1, 3, 7}});
|
|
|
|
EXPECT_THAT(GenerateInitialValues(domain, 1000),
|
|
Contains(Value(domain, {1, 3, 7})));
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, UniqueElementsVectorOf) {
|
|
for (const auto& value : GenerateValues(
|
|
UniqueElementsVectorOf(InRange(100, 1000)).WithMaxSize(5))) {
|
|
static_assert(std::is_same_v<decltype(value.user_value), std::vector<int>>);
|
|
ASSERT_THAT(value.user_value, SizeIs(Le(5)));
|
|
ASSERT_THAT(value.user_value, Each(IsInClosedRange(100, 1000)));
|
|
ASSERT_THAT(value.user_value, ElementsAreUnique());
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, UniqueElementsVectorOfElementOf) {
|
|
const std::initializer_list<std::string> candidates{
|
|
"bonjour", "ciao", "g'day mate", "guten tag", "hallo",
|
|
"hello", "hi", "hola", "konnichiwa", "marhaba",
|
|
"namaste", "olá", "salaam", "salve", "szia"};
|
|
const absl::flat_hash_set<std::string> candidates_set(candidates.begin(),
|
|
candidates.end());
|
|
const auto inner_domain = ElementOf(candidates);
|
|
for (const auto& value : GenerateValues(UniqueElementsVectorOf(inner_domain)
|
|
.WithMinSize(1)
|
|
.WithMaxSize(2))) {
|
|
static_assert(
|
|
std::is_same_v<decltype(value.user_value), std::vector<std::string>>);
|
|
ASSERT_THAT(value.user_value, SizeIs(IsInClosedRange(1, 2)));
|
|
ASSERT_THAT(value.user_value, ElementsAreUnique());
|
|
for (const auto& elem : value.user_value) {
|
|
ASSERT_THAT(candidates_set, Contains(elem));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, UniqueElementsVectorOfVectorOfInt) {
|
|
auto elements_domain = VectorOf(InRange(500, 1000)).WithSize(3);
|
|
auto domain = UniqueElementsVectorOf(elements_domain).WithSize(4);
|
|
for (const auto& value : GenerateValues(domain)) {
|
|
static_assert(std::is_same_v<decltype(value.user_value),
|
|
std::vector<std::vector<int>>>);
|
|
ASSERT_THAT(value.user_value, SizeIs(4));
|
|
ASSERT_THAT(value.user_value, ElementsAreUnique());
|
|
ASSERT_THAT(value.user_value,
|
|
Each(AllOf(SizeIs(3), Each(IsInClosedRange(500, 1000)))));
|
|
}
|
|
}
|
|
|
|
TEST(UniqueElementsVectorOf, ValidationRejectsInvalidValue) {
|
|
absl::BitGen bitgen;
|
|
|
|
auto domain_a = UniqueElementsVectorOf(InRange(0, 9)).WithMinSize(1);
|
|
auto domain_b = UniqueElementsVectorOf(InRange(10, 19)).WithMinSize(1);
|
|
|
|
Value value_a(domain_a, bitgen);
|
|
Value value_b(domain_b, bitgen);
|
|
|
|
ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value));
|
|
ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value));
|
|
|
|
EXPECT_THAT(
|
|
domain_a.ValidateCorpusValue(value_b.corpus_value),
|
|
IsInvalid(MatchesRegex(
|
|
R"(Invalid value in container at index 0 >> The value .+ is not InRange\(0, 9\))")));
|
|
EXPECT_THAT(
|
|
domain_b.ValidateCorpusValue(value_a.corpus_value),
|
|
IsInvalid(MatchesRegex(
|
|
R"(Invalid value in container at index 0 >> The value .+ is not InRange\(10, 19\))")));
|
|
}
|
|
|
|
TEST(UniqueElementsVectorOf, ValidationRejectsNonUniqueElements) {
|
|
auto domain = UniqueElementsVectorOf(InRange(1, 10));
|
|
Value value(domain, {1, 2, 2, 3});
|
|
|
|
EXPECT_THAT(
|
|
domain.ValidateCorpusValue(value.corpus_value),
|
|
IsInvalid(HasSubstr(R"(The container doesn't have unique elements)")));
|
|
}
|
|
|
|
TEST(UniqueElementsVectorOf, VerifyRoundTripThroughConversion) {
|
|
auto domain = UniqueElementsVectorOf(InRange(100, 1000)).WithMaxSize(5);
|
|
VerifyRoundTripThroughConversion(GenerateValues(domain), domain);
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, ArrayOfOne) {
|
|
// A domain of std::array<T, 1> values can be defined in two ways:
|
|
auto with_explicit_size = ArrayOf<1>(InRange(0.0, 1.0));
|
|
auto with_inferred_size = ArrayOf(InRange(0.0, 1.0));
|
|
|
|
for (const auto& value : GenerateValues(with_explicit_size)) {
|
|
static_assert(
|
|
std::is_same_v<decltype(value.user_value), std::array<double, 1>>);
|
|
ASSERT_THAT(value.user_value[0], IsInClosedRange(0.0, 1.0));
|
|
}
|
|
|
|
for (const auto& value : GenerateValues(with_inferred_size)) {
|
|
static_assert(
|
|
std::is_same_v<decltype(value.user_value), std::array<double, 1>>);
|
|
ASSERT_THAT(value.user_value[0], IsInClosedRange(0.0, 1.0));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, ArrayOfTwoFromTwoDomains) {
|
|
// Define a domain of std::array<T, 2> values, where each element comes from a
|
|
// different domain, though of course both domains produce values of the same
|
|
// type.
|
|
for (const auto& value :
|
|
GenerateValues(ArrayOf(InRange(1900, 2022), InRange(1, 12)))) {
|
|
ASSERT_THAT(value.user_value,
|
|
AllOf(SizeIs(2), ElementsAre(IsInClosedRange(1900, 2022),
|
|
IsInClosedRange(1, 12))));
|
|
}
|
|
}
|
|
|
|
TEST(ContainerCombinatorTest, ArrayOfThreeFromOneDomain) {
|
|
for (const auto& value : GenerateValues(ArrayOf<3>(InRange(-0.5, 0.5)))) {
|
|
using array_type = decltype(value.user_value);
|
|
static_assert(std::is_same_v<array_type, std::array<double, 3>>);
|
|
ASSERT_THAT(value.user_value,
|
|
AllOf(SizeIs(3), Each(IsInClosedRange(-0.5, 0.5))));
|
|
}
|
|
}
|
|
|
|
TEST(Domain, Container) {
|
|
Domain<std::vector<int>> domain =
|
|
ContainerOf<std::vector<int>>(InRange(1, 100));
|
|
for (const auto& value : GenerateValues(domain)) {
|
|
for (auto i : value.user_value) {
|
|
ASSERT_GE(i, 1);
|
|
ASSERT_LE(i, 100);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(TupleOf, GeneratesValidValues) {
|
|
auto values = MutateUntilFoundN(TupleOf(InRange(-5, 5), AsciiChar()), 100);
|
|
EXPECT_THAT(values, Each(FieldsAre(_, FieldsAre(IsInClosedRange(-5, 5),
|
|
IsInClosedRange(0, 127)))));
|
|
}
|
|
|
|
TEST(TupleOf, InitGeneratesSeeds) {
|
|
auto domain = TupleOf(Arbitrary<int>(), Arbitrary<std::string>())
|
|
.WithSeeds({{42, "hello"}});
|
|
|
|
EXPECT_THAT(GenerateInitialValues(domain, 1000),
|
|
Contains(Value(domain, {42, "hello"})));
|
|
}
|
|
|
|
TEST(PairOf, GeneratesValidValues) {
|
|
auto values = MutateUntilFoundN(PairOf(InRange(-5, 5), AsciiChar()), 100);
|
|
EXPECT_THAT(values, Each(FieldsAre(_, FieldsAre(IsInClosedRange(-5, 5),
|
|
IsInClosedRange(0, 127)))));
|
|
}
|
|
|
|
TEST(PairOf, InitGeneratesSeeds) {
|
|
auto domain = PairOf(Arbitrary<int>(), Arbitrary<std::string>())
|
|
.WithSeeds({{42, "hello"}});
|
|
|
|
EXPECT_THAT(GenerateInitialValues(domain, 1000),
|
|
Contains(Value(domain, {42, "hello"})));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace fuzztest
|
|
|