| // 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. |
| |
| #include "./fuzztest/internal/type_support.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <cmath> |
| #include <complex> |
| #include <limits> |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <ostream> |
| #include <set> |
| #include <string> |
| #include <string_view> |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_format.h" |
| #include "./fuzztest/domain.h" |
| #include "./fuzztest/internal/domain.h" |
| #include "./fuzztest/internal/protobuf_domain.h" |
| #include "./fuzztest/internal/test_protobuf.pb.h" |
| |
| namespace fuzztest::internal { |
| namespace { |
| |
| using ::testing::AllOf; |
| using ::testing::Contains; |
| using ::testing::Each; |
| using ::testing::ElementsAre; |
| using ::testing::EndsWith; |
| using ::testing::HasSubstr; |
| using ::testing::Le; |
| using ::testing::MatchesRegex; |
| |
| template <typename Domain> |
| std::vector<std::string> TestPrintValue(const corpus_type_t<Domain>& value, |
| const Domain& domain) { |
| std::vector<std::string> res(2); |
| PrintValue(domain, value, &res[0], PrintMode::kHumanReadable); |
| PrintValue(domain, value, &res[1], PrintMode::kSourceCode); |
| return res; |
| } |
| |
| template <typename T> |
| std::vector<std::string> TestPrintValue(const T& value) { |
| std::vector<std::string> res(2); |
| auto traits = AutodetectTypePrinter<T>(); |
| traits.PrintUserValue(value, &res[0], PrintMode::kHumanReadable); |
| traits.PrintUserValue(value, &res[1], PrintMode::kSourceCode); |
| return res; |
| } |
| |
| TEST(BoolTest, Printer) { |
| EXPECT_THAT(TestPrintValue(false), Each("false")); |
| EXPECT_THAT(TestPrintValue(true), Each("true")); |
| } |
| |
| TEST(CharTest, Printer) { |
| EXPECT_THAT(TestPrintValue('a'), ElementsAre("'a' (97)", "'a'")); |
| EXPECT_THAT(TestPrintValue(static_cast<char>(200)), |
| ElementsAre("0xc8 (200)", "'\\310'")); |
| EXPECT_THAT(TestPrintValue(static_cast<char>(1)), |
| ElementsAre("0x01 (1)", "'\\001'")); |
| } |
| |
| template <typename T> |
| class IntegralTest : public testing::Test {}; |
| |
| using IntegralTypes = testing::Types<signed char, unsigned char, // |
| short, unsigned short, // |
| int, unsigned int, // |
| long, unsigned long, // |
| long long, unsigned long long>; |
| |
| TYPED_TEST_SUITE(IntegralTest, IntegralTypes); |
| |
| TYPED_TEST(IntegralTest, Printer) { |
| for (auto v : {TypeParam{0}, std::numeric_limits<TypeParam>::min(), |
| std::numeric_limits<TypeParam>::max()}) { |
| EXPECT_THAT(TestPrintValue(v), Each(std::to_string(v))); |
| } |
| } |
| |
| enum Color { kRed, kBlue, kGreen }; |
| enum ColorChar : char { kRedChar = 'r', kBlueChar = 'b' }; |
| enum class ColorClass { kRed, kBlue, kGreen }; |
| enum class ColorClassChar : char { kRed = 'r', kBlue = 'b' }; |
| TEST(EnumTest, Printer) { |
| EXPECT_THAT(TestPrintValue(kRed), |
| ElementsAre("Color{0}", "static_cast<Color>(0)")); |
| EXPECT_THAT( |
| TestPrintValue(kRedChar), |
| ElementsAre("ColorChar{'r' (114)}", "static_cast<ColorChar>('r')")); |
| EXPECT_THAT(TestPrintValue(ColorClass::kRed), |
| ElementsAre("ColorClass{0}", "static_cast<ColorClass>(0)")); |
| EXPECT_THAT(TestPrintValue(ColorClassChar::kRed), |
| ElementsAre("ColorClassChar{'r' (114)}", |
| "static_cast<ColorClassChar>('r')")); |
| } |
| |
| template <typename T> |
| class FloatingTest : public testing::Test {}; |
| |
| using FloatingTypes = testing::Types<float, double, long double>; |
| |
| TYPED_TEST_SUITE(FloatingTest, FloatingTypes); |
| |
| TYPED_TEST(FloatingTest, Printer) { |
| std::string_view suffix = std::is_same_v<float, TypeParam> ? "f" |
| : std::is_same_v<double, TypeParam> ? "" |
| : "L"; |
| EXPECT_THAT(TestPrintValue(TypeParam{0}), Each(absl::StrCat("0.", suffix))); |
| for (auto v : {std::numeric_limits<TypeParam>::min(), |
| std::numeric_limits<TypeParam>::max()}) { |
| EXPECT_THAT(TestPrintValue(v), |
| Each(AllOf(HasSubstr("e"), Contains('.').Times(Le(1)), |
| EndsWith(suffix)))); |
| } |
| TypeParam inf = std::numeric_limits<TypeParam>::infinity(); |
| auto type = GetTypeName<TypeParam>(); |
| EXPECT_THAT(TestPrintValue(inf), |
| ElementsAre(absl::StrFormat("%f", inf), |
| absl::StrFormat("std::numeric_limits<%s>::infinity()", |
| type))); |
| EXPECT_THAT(TestPrintValue(-inf), |
| ElementsAre(absl::StrFormat("%f", -inf), |
| absl::StrFormat( |
| "-std::numeric_limits<%s>::infinity()", type))); |
| TypeParam nan = std::nan(""); |
| EXPECT_THAT( |
| TestPrintValue(nan), |
| ElementsAre(absl::StrFormat("%f", nan), |
| std::is_same_v<float, TypeParam> ? "std::nanf(\"\")" |
| : std::is_same_v<double, TypeParam> ? "std::nan(\"\")" |
| : "std::nanl(\"\")")); |
| } |
| |
| TEST(StringTest, Printer) { |
| EXPECT_THAT(TestPrintValue(std::string("ABC")), Each("\"ABC\"")); |
| EXPECT_THAT( |
| TestPrintValue(std::string{'\0', 'a', '\223', 'b'}), |
| ElementsAre(R"("\000a\223b")", R"(std::string("\000a\223b", 4))")); |
| EXPECT_THAT(TestPrintValue(std::string("printf(\"Hello, world!\");")), |
| ElementsAre(R"("printf("Hello, world!");")", |
| R"("printf(\"Hello, world!\");")")); |
| } |
| |
| TEST(CompoundTest, Printer) { |
| EXPECT_THAT( |
| TestPrintValue(std::pair(1, 1.5), Arbitrary<std::pair<int, double>>()), |
| Each("{1, 1.5}")); |
| EXPECT_THAT(TestPrintValue(std::tuple(2, -3, -0.0), |
| Arbitrary<std::tuple<int, int, double>>()), |
| Each("{2, -3, -0.}")); |
| |
| struct UserDefined { |
| int i; |
| double d; |
| std::string s; |
| }; |
| EXPECT_THAT(TestPrintValue( |
| std::tuple{2, -3.5, "Foo"}, |
| StructOf<UserDefined>(Arbitrary<int>(), Arbitrary<double>(), |
| Arbitrary<std::string>())), |
| Each("{2, -3.5, \"Foo\"}")); |
| } |
| |
| TEST(ProtobufTest, Printer) { |
| internal::TestProtobuf proto; |
| proto.set_b(true); |
| proto.add_rep_subproto()->set_subproto_i32(17); |
| EXPECT_THAT(TestPrintValue(proto), |
| ElementsAre(absl::StrCat("(", proto.ShortDebugString(), ")"), |
| absl::StrCat("ParseTestProto(R\"pb(", |
| proto.ShortDebugString(), ")pb\")"))); |
| } |
| |
| TEST(ProtobufEnumTest, Printer) { |
| auto domain = Arbitrary<internal::TestProtobuf_Enum>(); |
| EXPECT_THAT(TestPrintValue(TestProtobuf_Enum_Label2, domain), |
| ElementsAre("fuzztest::internal::TestProtobuf::Label2 (1)", |
| "fuzztest::internal::TestProtobuf::Label2")); |
| |
| domain = Arbitrary<internal::TestProtobuf::Enum>(); |
| EXPECT_THAT(TestPrintValue(TestProtobuf::Label3, domain), |
| ElementsAre("fuzztest::internal::TestProtobuf::Label3 (2)", |
| "fuzztest::internal::TestProtobuf::Label3")); |
| |
| EXPECT_THAT( |
| TestPrintValue(static_cast<internal::TestProtobuf_Enum>(100), domain), |
| ElementsAre("fuzztest::internal::TestProtobuf_Enum{100}", |
| "static_cast<fuzztest::internal::TestProtobuf_Enum>(100)")); |
| |
| auto bare_domain = Arbitrary<internal::BareEnum>(); |
| EXPECT_THAT(TestPrintValue(internal::BareEnum::LABEL_OTHER, bare_domain), |
| ElementsAre("fuzztest::internal::BareEnum::LABEL_OTHER (10)", |
| "fuzztest::internal::BareEnum::LABEL_OTHER")); |
| } |
| |
| TEST(ContainerTest, Printer) { |
| EXPECT_THAT( |
| TestPrintValue(std::vector{1, 2, 3}, Arbitrary<std::vector<int>>()), |
| Each("{1, 2, 3}")); |
| EXPECT_THAT(TestPrintValue(*Arbitrary<std::set<int>>().FromValue({1, 2, 3}), |
| Arbitrary<std::set<int>>()), |
| Each("{1, 2, 3}")); |
| EXPECT_THAT(TestPrintValue( |
| *Arbitrary<std::map<int, int>>().FromValue({{1, 2}, {2, 3}}), |
| Arbitrary<std::map<int, int>>()), |
| Each("{{1, 2}, {2, 3}}")); |
| |
| // With custom inner |
| auto inner = ElementOf({kGreen, kRed}); |
| using InnerCorpusT = corpus_type_t<decltype(inner)>; |
| EXPECT_THAT(TestPrintValue( |
| std::list{InnerCorpusT{0}, InnerCorpusT{0}, InnerCorpusT{1}}, |
| ContainerOf<std::vector<Color>>(inner)), |
| ElementsAre("{Color{2}, Color{2}, Color{0}}", |
| "{static_cast<Color>(2), static_cast<Color>(2), " |
| "static_cast<Color>(0)}")); |
| } |
| |
| TEST(DomainTest, Printer) { |
| // Make sure we can print through the type erased Domain<T> |
| auto color_domain = ElementOf({kBlue}); |
| auto print = [&](auto v, auto domain) { |
| // We have to create the inner corpus_type of Domain here. |
| return TestPrintValue( |
| typename decltype(domain)::corpus_type(std::in_place, v), domain); |
| }; |
| EXPECT_THAT(print('a', Domain<char>(Arbitrary<char>())), |
| ElementsAre("'a' (97)", "'a'")); |
| EXPECT_THAT(print(typename decltype(color_domain)::corpus_type{0}, |
| Domain<Color>(color_domain)), |
| ElementsAre("Color{1}", "static_cast<Color>(1)")); |
| } |
| |
| TEST(VariantTest, Printer) { |
| using V = std::variant<int, double, std::vector<std::string>>; |
| V value; |
| auto variant_domain = VariantOf<V>( |
| Arbitrary<int>(), Arbitrary<double>(), |
| ContainerOf<std::vector<std::string>>(Arbitrary<std::string>())); |
| value = 1; |
| EXPECT_THAT(TestPrintValue(value, variant_domain), |
| ElementsAre("(index=0, value=1)", "1")); |
| value = 1.2; |
| EXPECT_THAT(TestPrintValue(value, variant_domain), |
| ElementsAre("(index=1, value=1.2)", "1.2")); |
| value = std::vector<std::string>{"variant", "print", "test"}; |
| EXPECT_THAT(TestPrintValue(value, variant_domain), |
| ElementsAre("(index=2, value={\"variant\", \"print\", \"test\"})", |
| "{\"variant\", \"print\", \"test\"}")); |
| } |
| |
| TEST(OptionalTest, Printer) { |
| auto optional_int_domain = OptionalOf(Arbitrary<int>()); |
| EXPECT_THAT(TestPrintValue({}, optional_int_domain), Each("std::nullopt")); |
| EXPECT_THAT(TestPrintValue(1, optional_int_domain), ElementsAre("(1)", "1")); |
| |
| auto optional_string_domain = OptionalOf(Arbitrary<std::string>()); |
| EXPECT_THAT(TestPrintValue({}, optional_string_domain), Each("std::nullopt")); |
| EXPECT_THAT(TestPrintValue("ABC", optional_string_domain), |
| ElementsAre("(\"ABC\")", "\"ABC\"")); |
| } |
| |
| TEST(SmartPointerTest, Printer) { |
| EXPECT_THAT(TestPrintValue({}, Arbitrary<std::unique_ptr<int>>()), |
| Each("nullptr")); |
| EXPECT_THAT(TestPrintValue(Domain<int>::corpus_type(std::in_place, 7), |
| Arbitrary<std::unique_ptr<int>>()), |
| ElementsAre("(7)", "std::make_unique<int>(7)")); |
| EXPECT_THAT( |
| TestPrintValue( |
| Domain<std::string>::corpus_type(std::in_place, std::string("ABC")), |
| Arbitrary<std::shared_ptr<std::string>>()), |
| ElementsAre( |
| R"(("ABC"))", |
| MatchesRegex(R"re(std::make_shared<std::.*string.*>\("ABC"\))re"))); |
| } |
| |
| TEST(OneOfTest, Printer) { |
| auto domain = OneOf(ElementOf({17}), InRange(20, 22)); |
| using corpus_type = corpus_type_t<decltype(domain)>; |
| |
| EXPECT_THAT(TestPrintValue(corpus_type(std::in_place_index<0>), domain), |
| Each("17")); |
| EXPECT_THAT(TestPrintValue(corpus_type(std::in_place_index<1>, 21), domain), |
| Each("21")); |
| } |
| |
| std::string StringTimes(int n, char c) { return std::string(n, c); } |
| |
| auto HexString() { |
| return Map([](int number) { return absl::StrFormat("%#x", number); }, |
| InRange(0, 32)); |
| } |
| |
| TEST(MapTest, Printer) { |
| auto domain = Map(StringTimes, InRange(2, 5), InRange('a', 'b')); |
| std::tuple<int, char> corpus_value(3, 'b'); |
| |
| EXPECT_THAT(TestPrintValue(corpus_value, domain), |
| ElementsAre("\"bbb\"", |
| // Takes into account that the function name may |
| // contain ABI annotations after de-mangling. |
| MatchesRegex(R"re(StringTimes.*\(3, 'b'\))re"))); |
| |
| // Test fallback on user value when map involves a lambda. |
| EXPECT_THAT(TestPrintValue(std::tuple<int>(21), HexString()), |
| Each("\"0x15\"")); |
| } |
| |
| auto ValueInRange(int a, int b) { |
| int min = std::min(a, b); |
| int max = std::max(a, b); |
| return InRange(min, max); |
| } |
| |
| TEST(FlatMapTest, PrinterWithNamedFunction) { |
| auto domain = FlatMap(ValueInRange, Arbitrary<int>(), Arbitrary<int>()); |
| decltype(domain)::corpus_type corpus_value = {2, 3, 1}; |
| EXPECT_THAT(TestPrintValue(corpus_value, domain), |
| ElementsAre("2", "ValueInRange(3, 1)")); |
| } |
| |
| TEST(FlatMapTest, PrinterWithLambda) { |
| auto domain = |
| FlatMap([](int a) { return ValueInRange(a, a + 100); }, Arbitrary<int>()); |
| decltype(domain)::corpus_type corpus_value = {42, 0}; |
| EXPECT_THAT(TestPrintValue(corpus_value, domain), Each("42")); |
| } |
| |
| TEST(ConstructorOfTest, Printer) { |
| EXPECT_THAT( |
| TestPrintValue({3, 'b'}, ConstructorOf<std::string>(InRange(0, 5), |
| Arbitrary<char>())), |
| ElementsAre("\"bbb\"", MatchesRegex(R"re(std::.*string.*\(3, 'b'\))re"))); |
| EXPECT_THAT(TestPrintValue( |
| {-1}, ConstructorOf<std::complex<float>>(Arbitrary<float>())), |
| ElementsAre("(-1,0)", "std::complex<float>(-1.f)")); |
| |
| struct UserDefined {}; |
| EXPECT_THAT(TestPrintValue({}, ConstructorOf<UserDefined>()), |
| ElementsAre("{}", "UserDefined()")); |
| } |
| |
| TEST(MonostateTest, Printer) { |
| struct UserDefinedEmpty {}; |
| EXPECT_THAT(TestPrintValue(std::monostate{}), Each("{}")); |
| EXPECT_THAT(TestPrintValue(std::tuple{}), Each("{}")); |
| EXPECT_THAT(TestPrintValue(std::array<int, 0>{}), Each("{}")); |
| EXPECT_THAT(TestPrintValue(UserDefinedEmpty{}), Each("{}")); |
| } |
| |
| struct AggregateStructWithNoStream { |
| int i = 1; |
| std::pair<std::string, std::string> nested = {"Foo", "Bar"}; |
| }; |
| |
| struct AggregateStructWithStream { |
| int i = 1; |
| std::pair<std::string, std::string> nested = {"Foo", "Bar"}; |
| friend std::ostream& operator<<(std::ostream& os, |
| const AggregateStructWithStream& s) { |
| return os << "value={" << s.i << ", " |
| << "{" << s.nested.first << ", " << s.nested.second << "}}"; |
| } |
| }; |
| |
| TEST(AutodetectAggregateTest, Printer) { |
| // MonostateTest handles empty tuple and array. |
| |
| EXPECT_THAT(TestPrintValue(std::tuple{123}), Each("{123}")); |
| EXPECT_THAT(TestPrintValue(std::pair{123, 456}), Each("{123, 456}")); |
| EXPECT_THAT(TestPrintValue(std::array{123, 456}), Each("{123, 456}")); |
| EXPECT_THAT(TestPrintValue(AggregateStructWithNoStream{}), |
| Each(R"({1, {"Foo", "Bar"}})")); |
| EXPECT_THAT(TestPrintValue(AggregateStructWithStream{}), |
| ElementsAre("value={1, {Foo, Bar}}", R"({1, {"Foo", "Bar"}})")); |
| } |
| |
| struct NonAggregateStructWithNoStream { |
| NonAggregateStructWithNoStream() : i(1), nested("Foo", "Bar") {} |
| int i; |
| std::pair<std::string, std::string> nested; |
| }; |
| |
| struct NonAggregateStructWithStream { |
| NonAggregateStructWithStream() : i(1), nested("Foo", "Bar") {} |
| int i; |
| std::pair<std::string, std::string> nested; |
| friend std::ostream& operator<<(std::ostream& os, |
| const NonAggregateStructWithStream& s) { |
| return os << "value={" << s.i << ", " |
| << "{" << s.nested.first << ", " << s.nested.second << "}}"; |
| } |
| }; |
| |
| TEST(UnprintableTest, Printer) { |
| EXPECT_THAT(TestPrintValue(NonAggregateStructWithNoStream{}), |
| Each("<unprintable value>")); |
| EXPECT_THAT(TestPrintValue(NonAggregateStructWithStream{}), |
| ElementsAre("value={1, {Foo, Bar}}", "<unprintable value>")); |
| } |
| |
| } // namespace |
| } // namespace fuzztest::internal |