| // 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 StructOf, ConstructorOf, VariantOf and OptionalOf. |
| |
| #include <bitset> |
| #include <cctype> |
| #include <deque> |
| #include <iterator> |
| #include <list> |
| #include <optional> |
| #include <set> |
| #include <string> |
| #include <unordered_set> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/random/random.h" |
| #include "./fuzztest/domain.h" |
| #include "./domain_tests/domain_testing.h" |
| |
| namespace fuzztest { |
| namespace { |
| |
| using ::testing::_; |
| using ::testing::AnyOf; |
| using ::testing::Eq; |
| using ::testing::Optional; |
| using ::testing::UnorderedElementsAre; |
| using ::testing::VariantWith; |
| |
| struct MyStruct { |
| int a; |
| std::string s; |
| }; |
| |
| TEST(StructOf, InitGeneratesValidValues) { |
| auto domain = |
| StructOf<MyStruct>(ElementOf({5, 10}), Arbitrary<std::string>()); |
| absl::BitGen bitgen; |
| Set<int> field_a; |
| Set<std::string> field_s; |
| while (field_a.size() < 2 && field_s.size() < 10) { |
| auto agg = Value(domain, bitgen).user_value; |
| EXPECT_THAT(agg.a, AnyOf(5, 10)); |
| field_a.insert(agg.a); |
| field_s.insert(agg.s); |
| } |
| } |
| |
| TEST(StructOf, MutateGeneratesValidValues) { |
| auto domain = |
| StructOf<MyStruct>(ElementOf({5, 10}), Arbitrary<std::string>()); |
| absl::BitGen bitgen; |
| Set<int> field_a; |
| Set<std::string> field_s; |
| Value agg(domain, bitgen); |
| while (field_a.size() < 2 && field_s.size() < 10) { |
| agg.Mutate(domain, bitgen, false); |
| EXPECT_THAT(agg.user_value.a, AnyOf(5, 10)); |
| field_a.insert(agg.user_value.a); |
| field_s.insert(agg.user_value.s); |
| } |
| } |
| |
| TEST(StructOf, WorksWithStructWithUpTo16Fields) { |
| struct Agg16 { |
| uint32_t a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p; |
| uint32_t sum() { |
| return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p; |
| } |
| }; |
| auto a = Arbitrary<uint32_t>(); |
| absl::BitGen bitgen; |
| |
| // Just check that we can create and mutate the value. |
| auto domain = StructOf<Agg16>(a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a); |
| Value value(domain, bitgen); |
| const uint32_t initial_sum = value.user_value.sum(); |
| while (value.user_value.sum() == initial_sum) { |
| value.Mutate(domain, bitgen, false); |
| } |
| // Check that shrinking works too. |
| while (value.user_value.sum() != 0) { |
| value.Mutate(domain, bitgen, true); |
| } |
| } |
| |
| class MyClass { |
| public: |
| MyClass(int a, double d, std::string s) : a_(a), d_(d), s_(std::move(s)) {} |
| |
| int a() const { return a_; } |
| double d() const { return d_; } |
| std::string s() const { return s_; } |
| |
| private: |
| int a_; |
| double d_; |
| std::string s_; |
| }; |
| |
| TEST(ConstructorOf, InitGeneratesValidValues) { |
| auto domain = ConstructorOf<MyClass>(ElementOf({5, 10}), Arbitrary<double>(), |
| Arbitrary<std::string>()); |
| absl::BitGen bitgen; |
| Set<int> field_a; |
| Set<double> field_d; |
| Set<std::string> field_s; |
| while (field_a.size() < 2 && field_d.size() < 10 && field_s.size() < 10) { |
| auto agg = Value(domain, bitgen).user_value; |
| EXPECT_THAT(agg.a(), AnyOf(5, 10)); |
| field_a.insert(agg.a()); |
| field_d.insert(agg.d()); |
| field_s.insert(agg.s()); |
| } |
| } |
| |
| TEST(ConstructorOf, MutateGeneratesValidValues) { |
| auto domain = ConstructorOf<MyClass>(ElementOf({5, 10}), Arbitrary<double>(), |
| Arbitrary<std::string>()); |
| absl::BitGen bitgen; |
| Set<int> field_a; |
| Set<double> field_d; |
| Set<std::string> field_s; |
| Value agg(domain, bitgen); |
| while (field_a.size() < 2 && field_d.size() < 10 && field_s.size() < 10) { |
| agg.Mutate(domain, bitgen, false); |
| EXPECT_THAT(agg.user_value.a(), AnyOf(5, 10)); |
| field_a.insert(agg.user_value.a()); |
| field_d.insert(agg.user_value.d()); |
| field_s.insert(agg.user_value.s()); |
| } |
| } |
| |
| class HasTemplatedConstructor { |
| public: |
| template <typename... Args> |
| HasTemplatedConstructor(Args... args) { |
| sum_ = (args + ...); |
| } |
| int sum() const { return sum_; } |
| |
| private: |
| int sum_; |
| }; |
| |
| TEST(ConstructorOf, WorksWithTemplatedConstructors) { |
| auto a = Arbitrary<uint32_t>(); |
| absl::BitGen bitgen; |
| |
| // Just check that we can create and mutate the value. |
| auto domain = ConstructorOf<HasTemplatedConstructor>(a, a, a, a, a, a, a, a, |
| a, a, a, a, a, a, a, a); |
| Value value(domain, bitgen); |
| const uint32_t initial_sum = value.user_value.sum(); |
| while (value.user_value.sum() == initial_sum) { |
| value.Mutate(domain, bitgen, false); |
| } |
| // Check that shrinking works too. |
| while (value.user_value.sum() != 0) { |
| value.Mutate(domain, bitgen, true); |
| } |
| } |
| |
| TEST(VariantOf, InitGenerateValidValues) { |
| using X = std::variant<int, int, std::string>; |
| Domain<X> domain = |
| VariantOf(ElementOf({5, 10}), InRange(50, 500), PrintableAsciiString()); |
| absl::BitGen bitgen; |
| std::vector<absl::flat_hash_set<int>> int_unique_value_arr(2); |
| absl::flat_hash_set<std::string> string_unique_values; |
| constexpr int n = 20; |
| while (int_unique_value_arr[0].size() < 2 || |
| int_unique_value_arr[1].size() < n || |
| string_unique_values.size() < n) { |
| X value = Value(domain, bitgen).user_value; |
| if (value.index() == 0) { |
| int val = std::get<0>(value); |
| EXPECT_THAT(val, AnyOf(5, 10)); |
| int_unique_value_arr[0].insert(val); |
| } else if (value.index() == 1) { |
| int val = std::get<1>(value); |
| EXPECT_LE(val, 500); |
| EXPECT_GE(val, 50); |
| int_unique_value_arr[1].insert(val); |
| } else { |
| string_unique_values.insert(std::get<2>(value)); |
| } |
| } |
| } |
| |
| TEST(VariantOf, MutateGenerateValidValues) { |
| using X = std::variant<int, int, std::string, std::vector<int>>; |
| Domain<X> domain = |
| VariantOf(ElementOf({5, 10}), InRange(200, 500), PrintableAsciiString(), |
| ContainerOf<std::vector<int>>(InRange(1, 7))); |
| absl::BitGen bitgen; |
| std::vector<int> counter(4, 0); |
| absl::flat_hash_set<int> unique_values[2]; |
| constexpr int n = 20; |
| Value value(domain, bitgen); |
| while (unique_values[0].size() < 2 || unique_values[1].size() < n || |
| counter[2] < n || counter[3] < n) { |
| value.Mutate(domain, bitgen, false); |
| if (value.user_value.index() == 0) { |
| int val = std::get<0>(value.user_value); |
| EXPECT_THAT(val, AnyOf(5, 10)); |
| unique_values[0].insert(val); |
| } else if (value.user_value.index() == 1) { |
| int val = std::get<1>(value.user_value); |
| EXPECT_LE(val, 500); |
| EXPECT_GE(val, 200); |
| unique_values[1].insert(val); |
| } |
| ++counter[value.user_value.index()]; |
| } |
| } |
| |
| TEST(VariantOf, WorksWithACustomVariantType) { |
| auto domain = VariantOf<absl::variant<int, double>>(Arbitrary<int>(), |
| Arbitrary<double>()); |
| absl::BitGen bitgen; |
| absl::variant<int, double> v = Value(domain, bitgen).user_value; |
| EXPECT_THAT(v, AnyOf(VariantWith<int>(_), VariantWith<double>(_))); |
| } |
| |
| TEST(OptionalOf, InitCanMakeValuesOrNull) { |
| auto domain = OptionalOf(InRange(1, 3)); |
| Set<std::optional<int>> values; |
| absl::BitGen bitgen; |
| while (values.size() < 4) { |
| values.insert(Value(domain, bitgen).user_value); |
| } |
| EXPECT_THAT(values, UnorderedElementsAre(std::nullopt, Optional(1), |
| Optional(2), Optional(3))); |
| } |
| |
| TEST(OptionalOf, MutateCanMakeValuesOrNull) { |
| auto domain = OptionalOf(InRange(1, 3)); |
| Set<std::optional<int>> values; |
| absl::BitGen bitgen; |
| Value value(domain, bitgen); |
| while (values.size() < 4) { |
| value.Mutate(domain, bitgen, false); |
| values.insert(value.user_value); |
| } |
| EXPECT_THAT(values, UnorderedElementsAre(std::nullopt, Optional(1), |
| Optional(2), Optional(3))); |
| } |
| |
| TEST(OptionalOf, WorksWithACustomOptionalType) { |
| auto domain = OptionalOf<absl::optional<int>>(InRange(1, 3)); |
| absl::BitGen bitgen; |
| absl::optional<int> v = Value(domain, bitgen).user_value; |
| EXPECT_THAT(v, AnyOf(absl::nullopt, Optional(_))); |
| } |
| |
| TEST(OptionalOf, AlwaysGenerateNulloptWhenPolicySet) { |
| auto domain = NullOpt<int>(); |
| |
| absl::BitGen bitgen; |
| |
| Value value(domain, bitgen); |
| for (int i = 0; i < 10000; ++i) { |
| EXPECT_TRUE(value == std::nullopt) << "Didn't expect non-null value!"; |
| value.Mutate(domain, bitgen, false); |
| } |
| } |
| |
| TEST(OptionalOf, DoesntGenerateNulloptWhenPolicySet) { |
| auto domain = NonNull(OptionalOf(Arbitrary<int>())); |
| |
| absl::BitGen bitgen; |
| |
| Value value(domain, bitgen); |
| for (int i = 0; i < 10000; ++i) { |
| EXPECT_TRUE(value != std::nullopt) << "Didn't expect null value!"; |
| value.Mutate(domain, bitgen, false); |
| } |
| } |
| |
| TEST(OptionalOfDeathTest, FromValueOnNulloptDiesWhenPolicySetToAlwaysSet) { |
| auto domain = NonNull(OptionalOf(Arbitrary<int>())); |
| EXPECT_DEATH(domain.FromValue(std::nullopt), "cannot be null"); |
| } |
| |
| } // namespace |
| } // namespace fuzztest |