// 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 #include #include #include #include #include #include #include #include #include #include #include #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(Positive()).WithSize(3))) { static_assert(std::is_same_v>); ASSERT_THAT(value.user_value, SizeIs(3)); ASSERT_THAT(value.user_value, Each(Ge(1))); } } TEST(ContainerCombinatorTest, ValueTypeOfVectorContainerIsInferred) { for (const auto& value : GenerateValues(ContainerOf(InRange(-6.0F, 6.0F)) .WithMinSize(2) .WithMaxSize(5))) { static_assert( std::is_same_v>); 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(NonEmpty(PrintableAsciiString())))) { static_assert( std::is_same_v>); 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(Arbitrary()) .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(Arbitrary()) .WithMinSize(100) .WithMaxSize(99), "Maximal size 99 cannot be smaller than minimal size 100"); EXPECT_DEATH_IF_SUPPORTED( ContainerOf(Arbitrary()) .WithMaxSize(100) .WithMinSize(101), "Minimal size 101 cannot be larger than maximal size 100"); } TEST(SequencedContainerTest, InitGeneratesSeeds) { auto domain = ContainerOf(Arbitrary()).WithSeeds({{1, 3, 3, 7}}); EXPECT_THAT(GenerateInitialValues(domain, 1000), Contains(Value(domain, {1, 3, 3, 7}))); } TEST(AssociativeContainerTest, InitGeneratesSeeds) { auto domain = ContainerOf>( PairOf(Arbitrary(), Arbitrary())) .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>); 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>); 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>); 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>); 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>( PairOf(std::move(key_domain), std::move(value_domain))) .WithMinSize(10) .WithMaxSize(50))) { static_assert(std::is_same>::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; absl::flat_hash_set> copy(arg.begin(), arg.end()); return arg.size() == copy.size(); } TEST(ContainerCombinatorTest, UniqueElementsContainerOf) { // An std::unordered_multiset 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 values. for (const auto& value : GenerateValues(UniqueElementsContainerOf>( InRange(1, 100)) .WithSize(7))) { static_assert(std::is_same_v>); ASSERT_THAT(value.user_value, SizeIs(7)); ASSERT_THAT(value.user_value, Each(IsInClosedRange(1, 100))); } } TEST(UniqueElementsContainerTest, InitGeneratesSeeds) { auto domain = UniqueElementsContainerOf>(Arbitrary()) .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>); 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 candidates{ "bonjour", "ciao", "g'day mate", "guten tag", "hallo", "hello", "hi", "hola", "konnichiwa", "marhaba", "namaste", "olá", "salaam", "salve", "szia"}; const absl::flat_hash_set 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>); 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>>); 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 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>); 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>); ASSERT_THAT(value.user_value[0], IsInClosedRange(0.0, 1.0)); } } TEST(ContainerCombinatorTest, ArrayOfTwoFromTwoDomains) { // Define a domain of std::array 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>); ASSERT_THAT(value.user_value, AllOf(SizeIs(3), Each(IsInClosedRange(-0.5, 0.5)))); } } TEST(Domain, Container) { Domain> domain = ContainerOf>(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(), Arbitrary()) .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(), Arbitrary()) .WithSeeds({{42, "hello"}}); EXPECT_THAT(GenerateInitialValues(domain, 1000), Contains(Value(domain, {42, "hello"}))); } } // namespace } // namespace fuzztest