Add error propagation for ValidateCorpusValue(). By returning absl::Status with detailed error message. PiperOrigin-RevId: 551661565
diff --git a/domain_tests/BUILD b/domain_tests/BUILD index cd576dc..399a121 100644 --- a/domain_tests/BUILD +++ b/domain_tests/BUILD
@@ -22,6 +22,7 @@ name = "domain_testing", testonly = 1, hdrs = ["domain_testing.h"], + visibility = ["@com_google_fuzztest//fuzztest:__subpackages__"], deps = [ "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/hash", @@ -60,13 +61,11 @@ srcs = ["arbitrary_domains_test.cc"], deps = [ ":domain_testing", - "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/random", "@com_google_absl//absl/random:bit_gen_ref", "@com_google_absl//absl/status", "@com_google_absl//absl/time", - "@com_google_absl//absl/types:span", "@com_google_fuzztest//fuzztest:absl_helpers", "@com_google_fuzztest//fuzztest:domain", "@com_google_fuzztest//fuzztest:domain_core",
diff --git a/domain_tests/CMakeLists.txt b/domain_tests/CMakeLists.txt index fa00b22..5cac4e4 100644 --- a/domain_tests/CMakeLists.txt +++ b/domain_tests/CMakeLists.txt
@@ -40,6 +40,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging absl::flat_hash_set absl::optional @@ -66,6 +67,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging protobuf::libprotobuf test_protobuf @@ -96,6 +98,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging absl::flat_hash_map absl::flat_hash_set @@ -122,6 +125,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging absl::flat_hash_set absl::random_random @@ -149,6 +153,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging absl::random_random absl::flat_hash_set @@ -176,6 +181,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging absl::flat_hash_set absl::random_random @@ -201,6 +207,7 @@ fuzztest_type_support fuzztest_meta fuzztest_serialization + fuzztest_status fuzztest_logging absl::flat_hash_set absl::int128 @@ -226,6 +233,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging absl::flat_hash_set absl::int128 @@ -253,6 +261,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging absl::random_random GTest::gmock_main @@ -275,6 +284,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging absl::random_random GTest::gmock_main @@ -297,6 +307,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging absl::flat_hash_set absl::random_random @@ -320,6 +331,7 @@ fuzztest_coverage fuzztest_type_support fuzztest_serialization + fuzztest_status fuzztest_logging absl::flat_hash_set absl::random_random
diff --git a/domain_tests/aggregate_combinators_test.cc b/domain_tests/aggregate_combinators_test.cc index f4cfbb9..49d80ce 100644 --- a/domain_tests/aggregate_combinators_test.cc +++ b/domain_tests/aggregate_combinators_test.cc
@@ -270,11 +270,17 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for variant domain >> The value .+ is not InRange\(.+\))"))); + EXPECT_THAT( + domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for variant domain >> The value .+ is not InRange\(.+\))"))); } TEST(OptionalOf, InitCanMakeValuesOrNull) { @@ -355,11 +361,13 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT(domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid("Optional value must be set")); + EXPECT_THAT(domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid("Optional value must be null")); } TEST(OptionalOf, ValidationRejectsInvalidInnerValue) { @@ -371,11 +379,15 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::MatchesRegex(R"(The value .+ is not InRange\(.+\))"))); + EXPECT_THAT( + domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid(testing::MatchesRegex(R"(The value .+ is not InRange\(.+\))"))); } TEST(TupleOf, ValidationRejectsInvalidInnerValue) { @@ -387,11 +399,17 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value in aggregate >> The value .+ is not InRange\(.+\))"))); + EXPECT_THAT( + domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value in aggregate >> The value .+ is not InRange\(.+\))"))); } } // namespace
diff --git a/domain_tests/arbitrary_domains_test.cc b/domain_tests/arbitrary_domains_test.cc index b3d417d..82aac87 100644 --- a/domain_tests/arbitrary_domains_test.cc +++ b/domain_tests/arbitrary_domains_test.cc
@@ -18,7 +18,6 @@ #include <cmath> #include <cstdint> #include <limits> -#include <list> #include <memory> #include <optional> #include <string> @@ -33,13 +32,11 @@ #include "google/protobuf/util/message_differencer.h" #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/bit_gen_ref.h" #include "absl/random/random.h" #include "absl/status/status.h" #include "absl/time/time.h" -#include "absl/types/span.h" #include "./fuzztest/domain.h" #include "./domain_tests/domain_testing.h" #include "./fuzztest/internal/domains/absl_helpers.h" @@ -232,7 +229,9 @@ return internal::IRObject::FromCorpus(v); } - bool ValidateCorpusValue(const corpus_type&) const { return true; } + absl::Status ValidateCorpusValue(const corpus_type&) const { + return absl::OkStatus(); + } auto GetPrinter() const { return internal::IntegralPrinter{}; } @@ -625,13 +624,17 @@ auto domain_with_optional_always_set = Arbitrary<internal::TestSubProtobuf>().WithOptionalFieldsAlwaysSet(); auto corpus_value = domain_with_optional_always_set.FromValue(user_value); - EXPECT_FALSE( - domain_with_optional_always_set.ValidateCorpusValue(*corpus_value)); + EXPECT_THAT( + domain_with_optional_always_set.ValidateCorpusValue(*corpus_value), + IsInvalid("Invalid value for field subproto_i32 >> Optional value must " + "be set")); auto domain_with_repeated_always_set = Arbitrary<internal::TestSubProtobuf>().WithRepeatedFieldsAlwaysSet(); - EXPECT_FALSE(domain_with_repeated_always_set.ValidateCorpusValue( - *domain_with_optional_always_set.FromValue(user_value))); + EXPECT_THAT(domain_with_repeated_always_set.ValidateCorpusValue( + *domain_with_optional_always_set.FromValue(user_value)), + IsInvalid("Invalid value for field subproto_rep_i32 >> Invalid " + "size: 0. Min size: 1")); } TEST(ProtocolBuffer, SerializeAndParseCanHandleExtensions) { @@ -661,11 +664,15 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid("Invalid value for field i32 >> Optional value must be set")); + EXPECT_THAT( + domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid("Invalid value for field i32 >> Optional value must be null")); } TEST(ProtocolBuffer, ValidationRejectsUnexpectedRepeatedField) { @@ -683,11 +690,17 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for field rep_i32 >> Invalid size: .+. Min size: 1)"))); + EXPECT_THAT( + domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for field rep_i32 >> Invalid size: .+. Max size: 0)"))); } TEST(ProtocolBufferEnum, Arbitrary) {
diff --git a/domain_tests/container_combinators_test.cc b/domain_tests/container_combinators_test.cc index e69155e..fb79c34 100644 --- a/domain_tests/container_combinators_test.cc +++ b/domain_tests/container_combinators_test.cc
@@ -196,11 +196,13 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT(domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid("Invalid size: 3. Max size: 2")); + EXPECT_THAT(domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid("Invalid size: 2. Min size: 3")); } TEST(Container, ValidationRejectsInvalidElements) { @@ -212,11 +214,17 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::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(testing::MatchesRegex( + R"(Invalid value in container at index 0 >> The value .+ is not InRange\(10, 12\))"))); } TEST(ContainerCombinatorTest, ValueTypeOfListContainerIsInferred) { @@ -461,11 +469,17 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::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(testing::MatchesRegex( + R"(Invalid value in container at index 0 >> The value .+ is not InRange\(10, 19\))"))); } TEST(ContainerCombinatorTest, ArrayOfOne) {
diff --git a/domain_tests/domain_testing.h b/domain_tests/domain_testing.h index a1948c9..32006ff 100644 --- a/domain_tests/domain_testing.h +++ b/domain_tests/domain_testing.h
@@ -46,6 +46,28 @@ namespace fuzztest { +// Status matchers. + +MATCHER_P(StatusIs, status_code, "") { return arg.code() == status_code; } + +MATCHER_P2(StatusIs, status_code, message, "") { + return (arg.code() == status_code) && + testing::Matches(message)(std::string(arg.message())); +} + +MATCHER_P(IsInvalid, message, "") { + return testing::ExplainMatchResult( + StatusIs(absl::StatusCode::kInvalidArgument, message), arg, + result_listener); +} + +#ifndef ASSERT_OK +#define ASSERT_OK(x) ASSERT_THAT(x, StatusIs(absl::StatusCode::kOk)) +#endif // ASSERT_OK +#ifndef EXPECT_OK +#define EXPECT_OK(x) EXPECT_THAT(x, StatusIs(absl::StatusCode::kOk)) +#endif // EXPECT_OK + // Tests whether arg is in the range [a, b]. MATCHER_P2(IsInClosedRange, a, b, absl::StrCat(negation ? "isn't" : "is", " in the closed range [", @@ -206,7 +228,7 @@ { auto corpus_value = domain.FromValue(v.user_value); ASSERT_TRUE(corpus_value) << v; - ASSERT_TRUE(domain.ValidateCorpusValue(*corpus_value)); + ASSERT_OK(domain.ValidateCorpusValue(*corpus_value)); auto new_v = domain.GetValue(*corpus_value); EXPECT_TRUE(Eq{}(v.user_value, new_v)) << "v=" << v << " new_v=" << testing::PrintToString(new_v); @@ -218,7 +240,7 @@ auto parsed_corpus = domain.ParseCorpus(*parsed); ASSERT_TRUE(parsed_corpus) << serialized << " value = " << testing::PrintToString(v.user_value); - ASSERT_TRUE(domain.ValidateCorpusValue(*parsed_corpus)); + ASSERT_OK(domain.ValidateCorpusValue(*parsed_corpus)); EXPECT_TRUE(Eq{}(v.user_value, domain.GetValue(*parsed_corpus))); } }
diff --git a/domain_tests/in_regexp_domain_test.cc b/domain_tests/in_regexp_domain_test.cc index 081bf95..054c9ae 100644 --- a/domain_tests/in_regexp_domain_test.cc +++ b/domain_tests/in_regexp_domain_test.cc
@@ -180,11 +180,13 @@ auto corpus_value_a = domain_a.FromValue("acceeb"); auto corpus_value_b = domain_b.FromValue("AADD"); - ASSERT_TRUE(domain_a.ValidateCorpusValue(*corpus_value_a)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(*corpus_value_b)); + ASSERT_OK(domain_a.ValidateCorpusValue(*corpus_value_a)); + ASSERT_OK(domain_b.ValidateCorpusValue(*corpus_value_b)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(*corpus_value_b)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(*corpus_value_a)); + EXPECT_THAT(domain_a.ValidateCorpusValue(*corpus_value_b), + IsInvalid("Invalid value for InRegexp(\"a(c|d)+(e|f)+b\")")); + EXPECT_THAT(domain_b.ValidateCorpusValue(*corpus_value_a), + IsInvalid("Invalid value for InRegexp(\"A{2,10}D{2,10}\")")); } struct InRegexString { @@ -198,7 +200,7 @@ auto domain = InRegexp(regexp); auto corpus_value = domain.FromValue(string_in_domain); ASSERT_TRUE(corpus_value.has_value()); - ASSERT_TRUE(domain.ValidateCorpusValue(*corpus_value)); + ASSERT_OK(domain.ValidateCorpusValue(*corpus_value)); EXPECT_THAT(domain.ParseCorpus(domain.SerializeCorpus(*corpus_value)), Optional(ResultOf(
diff --git a/domain_tests/map_filter_combinator_test.cc b/domain_tests/map_filter_combinator_test.cc index a74328d..04afeff 100644 --- a/domain_tests/map_filter_combinator_test.cc +++ b/domain_tests/map_filter_combinator_test.cc
@@ -88,11 +88,17 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for Map\(\)-ed domain >> The value .+ is not InRange\(0, 9\))"))); + EXPECT_THAT( + domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for Map\(\)-ed domain >> The value .+ is not InRange\(10, 19\))"))); } TEST(BidiMap, WorksWhenMapFunctionHasSameDomainAndRange) { @@ -117,11 +123,17 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for BidiMap\(\)-ed domain >> The value .+ is not InRange\(0, 9\))"))); + EXPECT_THAT( + domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for BidiMap\(\)-ed domain >> The value .+ is not InRange\(10, 19\))"))); } TEST(BidiMap, AcceptsMultipleInnerDomains) { @@ -211,11 +223,17 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for FlatMap\(\)-ed domain >> The value .+ is not InRange\(0, 9\))"))); + EXPECT_THAT( + domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for FlatMap\(\)-ed domain >> The value .+ is not InRange\(10, 19\))"))); } TEST(FlatMap, MutationAcceptsChangingDomains) { @@ -326,11 +344,13 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT(domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid("Value does not match Filter() predicate.")); + EXPECT_THAT(domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid("Value does not match Filter() predicate.")); } } // namespace
diff --git a/domain_tests/misc_domains_test.cc b/domain_tests/misc_domains_test.cc index c78c3db..0bfaac7 100644 --- a/domain_tests/misc_domains_test.cc +++ b/domain_tests/misc_domains_test.cc
@@ -180,11 +180,17 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for OneOf\(\) domain >> The value .+ is not InRange(.+))"))); + EXPECT_THAT( + domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid(testing::MatchesRegex( + R"(Invalid value for OneOf\(\) domain >> The value .+ is not InRange(.+))"))); } TEST(OneOf, FromValueReturnsValidCorpusValuesWhenPossible) { @@ -192,7 +198,7 @@ auto corpus_value = domain.FromValue(6); ASSERT_TRUE(corpus_value.has_value()); - EXPECT_TRUE(domain.ValidateCorpusValue(*corpus_value)); + EXPECT_OK(domain.ValidateCorpusValue(*corpus_value)); } } // namespace
diff --git a/domain_tests/numeric_domains_test.cc b/domain_tests/numeric_domains_test.cc index 9d8a3d7..7853f55 100644 --- a/domain_tests/numeric_domains_test.cc +++ b/domain_tests/numeric_domains_test.cc
@@ -206,11 +206,15 @@ Value value_a(domain_a, bitgen); Value value_b(domain_b, bitgen); - ASSERT_TRUE(domain_a.ValidateCorpusValue(value_a.corpus_value)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(value_b.corpus_value)); + ASSERT_OK(domain_a.ValidateCorpusValue(value_a.corpus_value)); + ASSERT_OK(domain_b.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(value_b.corpus_value)); - EXPECT_FALSE(domain_b.ValidateCorpusValue(value_a.corpus_value)); + EXPECT_THAT( + domain_a.ValidateCorpusValue(value_b.corpus_value), + IsInvalid(testing::MatchesRegex(R"(The value .+ is not InRange\(.+\))"))); + EXPECT_THAT( + domain_b.ValidateCorpusValue(value_a.corpus_value), + IsInvalid(testing::MatchesRegex(R"(The value .+ is not InRange\(.+\))"))); } TYPED_TEST(NumericTest, InRangeValueIsParsedCorrectly) { @@ -231,7 +235,7 @@ "FUZZTESTv1 ", absl::Substitute(serialized_format, static_cast<int32_t>(max))))); ASSERT_TRUE(corpus_value.has_value()); - EXPECT_TRUE(domain.ValidateCorpusValue(*corpus_value)); + EXPECT_OK(domain.ValidateCorpusValue(*corpus_value)); corpus_value = domain.ParseCorpus(*internal::IRObject::FromString(absl::StrCat( @@ -239,7 +243,9 @@ absl::Substitute(serialized_format, static_cast<int32_t>(max) + 1)))); // Greater than max should be parsed, but rejected by validation. ASSERT_TRUE(corpus_value.has_value()); - EXPECT_FALSE(domain.ValidateCorpusValue(*corpus_value)); + EXPECT_THAT( + domain.ValidateCorpusValue(*corpus_value), + IsInvalid(testing::MatchesRegex(R"(The value .+ is not InRange\(.+\))"))); } TYPED_TEST(NumericTest, NonZero) {
diff --git a/domain_tests/specific_value_domains_test.cc b/domain_tests/specific_value_domains_test.cc index ca272dd..363b611 100644 --- a/domain_tests/specific_value_domains_test.cc +++ b/domain_tests/specific_value_domains_test.cc
@@ -106,10 +106,11 @@ auto corpus_value_a = domain_a.FromValue('a'); auto corpus_value_b = domain_b.FromValue('c'); - ASSERT_TRUE(domain_a.ValidateCorpusValue(*corpus_value_a)); - ASSERT_TRUE(domain_b.ValidateCorpusValue(*corpus_value_b)); + ASSERT_OK(domain_a.ValidateCorpusValue(*corpus_value_a)); + ASSERT_OK(domain_b.ValidateCorpusValue(*corpus_value_b)); - EXPECT_FALSE(domain_a.ValidateCorpusValue(*corpus_value_b)); + EXPECT_THAT(domain_a.ValidateCorpusValue(*corpus_value_b), + IsInvalid("Invalid ElementOf() value")); } TEST(Just, Basic) {
diff --git a/e2e_tests/testdata/CMakeLists.txt b/e2e_tests/testdata/CMakeLists.txt index 5bf6546..dbc3f1d 100644 --- a/e2e_tests/testdata/CMakeLists.txt +++ b/e2e_tests/testdata/CMakeLists.txt
@@ -9,6 +9,7 @@ target_link_libraries( fuzz_tests_for_microbenchmarking PUBLIC + fuzztest_status protobuf::libprotobuf test_protobuf ) @@ -29,6 +30,7 @@ absl::str_format absl::time fuzztest_googletest_fixture_adapter + fuzztest_status ) link_fuzztest(fuzz_tests_for_functional_testing.stripped) set_target_properties( @@ -41,6 +43,11 @@ fuzz_tests_with_invalid_seeds.stripped fuzz_tests_with_invalid_seeds.cc ) +target_link_libraries( + fuzz_tests_with_invalid_seeds.stripped + PUBLIC + fuzztest_status +) link_fuzztest(fuzz_tests_with_invalid_seeds.stripped) set_target_properties( fuzz_tests_with_invalid_seeds.stripped
diff --git a/fuzztest/BUILD b/fuzztest/BUILD index f4d42ca..041e9cf 100644 --- a/fuzztest/BUILD +++ b/fuzztest/BUILD
@@ -136,6 +136,7 @@ ":logging", ":meta", ":serialization", + ":status", ":table_of_recent_compares", ":type_support", "@com_google_absl//absl/container:flat_hash_map", @@ -145,6 +146,7 @@ "@com_google_absl//absl/random", "@com_google_absl//absl/random:bit_gen_ref", "@com_google_absl//absl/random:distributions", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", @@ -170,6 +172,7 @@ ":meta", ":regexp_dfa", ":serialization", + ":status", ":type_support", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", @@ -365,6 +368,7 @@ "@com_google_absl//absl/random", "@com_google_absl//absl/random:bit_gen_ref", "@com_google_absl//absl/random:distributions", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", @@ -432,6 +436,16 @@ ) cc_library( + name = "status", + srcs = ["internal/status.cc"], + hdrs = ["internal/status.h"], + deps = [ + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + ], +) + +cc_library( name = "subprocess", srcs = ["internal/subprocess.cc"], hdrs = ["internal/subprocess.h"],
diff --git a/fuzztest/CMakeLists.txt b/fuzztest/CMakeLists.txt index ea451ee..61c06de 100644 --- a/fuzztest/CMakeLists.txt +++ b/fuzztest/CMakeLists.txt
@@ -135,6 +135,7 @@ fuzztest_registration fuzztest_logging fuzztest_coverage + fuzztest_status fuzztest_type_support protobuf::libprotobuf test_protobuf @@ -273,6 +274,7 @@ PUBLIC fuzztest_domain fuzztest_meta + fuzztest_status fuzztest_type_support absl::any_invocable absl::str_format @@ -294,6 +296,7 @@ fuzztest_logging fuzztest_registration fuzztest_type_support + fuzztest_status absl::memory absl::str_format absl::span @@ -313,6 +316,7 @@ fuzztest_registration fuzztest_logging fuzztest_coverage + fuzztest_status fuzztest_type_support absl::span GTest::gmock_main @@ -357,6 +361,7 @@ fuzztest_seed_seq fuzztest_fixture_driver fuzztest_serialization + fuzztest_status fuzztest_io fuzztest_type_support protobuf::libprotobuf @@ -374,6 +379,20 @@ endif () add_library( + fuzztest_status + OBJECT + internal/status.cc + internal/status.h +) + +target_link_libraries( + fuzztest_status + PUBLIC + absl::status + absl::strings +) + +add_library( fuzztest_subprocess OBJECT internal/subprocess.cc @@ -447,18 +466,20 @@ fuzztest_meta fuzztest_regexp_dfa fuzztest_serialization + fuzztest_status fuzztest_table_of_recent_compares fuzztest_type_support absl::base + absl::bits absl::flat_hash_map absl::flat_hash_set - absl::bits absl::int128 absl::random_distributions - absl::strings - absl::str_format - absl::synchronization absl::span + absl::status + absl::str_format + absl::strings + absl::synchronization ) add_library( @@ -494,6 +515,7 @@ fuzztest_io fuzztest_type_support fuzztest_runtime + fuzztest_status absl::time GTest::gmock_main ) @@ -522,12 +544,14 @@ fuzztest_logging fuzztest_meta fuzztest_registration + fuzztest_status fuzztest_serialization fuzztest_type_support absl::any_invocable absl::function_ref absl::random_random absl::random_seed_sequences + absl::status absl::strings absl::str_format absl::time @@ -551,6 +575,7 @@ fuzztest_io fuzztest_type_support fuzztest_runtime + fuzztest_status absl::time test_protobuf GTest::gtest_main @@ -674,6 +699,7 @@ fuzztest_runtime fuzztest_table_of_recent_compares fuzztest_subprocess + fuzztest_status fuzztest_compatibility_mode absl::flags absl::flags_parse
diff --git a/fuzztest/domain_core.h b/fuzztest/domain_core.h index 0061281..0b36b6e 100644 --- a/fuzztest/domain_core.h +++ b/fuzztest/domain_core.h
@@ -147,7 +147,7 @@ // After creating a corpus value, either via ParseCorpus() or via FromValue() // this method is used to determine if the corpus value is valid. - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { return inner_->UntypedValidateCorpusValue(corpus_value); } @@ -303,7 +303,7 @@ return GetInnerDomain().SerializeCorpus(v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { return GetInnerDomain().ValidateCorpusValue(corpus_value); } @@ -358,7 +358,7 @@ return inner_.SerializeCorpus(v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { return inner_.ValidateCorpusValue(corpus_value); }
diff --git a/fuzztest/internal/domains/aggregate_of_impl.h b/fuzztest/internal/domains/aggregate_of_impl.h index 9d73903..16dcd33 100644 --- a/fuzztest/internal/domains/aggregate_of_impl.h +++ b/fuzztest/internal/domains/aggregate_of_impl.h
@@ -29,6 +29,7 @@ #include "./fuzztest/internal/domains/serialization_helpers.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -182,12 +183,19 @@ } } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { - return ApplyIndex<sizeof...(Inner)>([&](auto... I) { - return ( - std::get<I>(inner_).ValidateCorpusValue(std::get<I>(corpus_value)) && + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status result = absl::OkStatus(); + ApplyIndex<sizeof...(Inner)>([&](auto... I) { + ( + [&] { + if (!result.ok()) return; + const absl::Status s = std::get<I>(inner_).ValidateCorpusValue( + std::get<I>(corpus_value)); + result = Prefix(s, "Invalid value in aggregate"); + }(), ...); }); + return result; } private:
diff --git a/fuzztest/internal/domains/arbitrary_impl.h b/fuzztest/internal/domains/arbitrary_impl.h index 71e10ed..89a3d26 100644 --- a/fuzztest/internal/domains/arbitrary_impl.h +++ b/fuzztest/internal/domains/arbitrary_impl.h
@@ -47,6 +47,7 @@ #include "./fuzztest/internal/domains/variant_of_impl.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/table_of_recent_compares.h" #include "./fuzztest/internal/type_support.h" @@ -73,8 +74,8 @@ void Mutate(value_type&, absl::BitGenRef, bool) {} - bool ValidateCorpusValue(const value_type&) const { - return true; // Nothing to validate. + absl::Status ValidateCorpusValue(const value_type&) const { + return absl::OkStatus(); // Nothing to validate. } auto GetPrinter() const { return MonostatePrinter{}; } @@ -97,8 +98,8 @@ } } - bool ValidateCorpusValue(const value_type&) const { - return true; // Nothing to validate. + absl::Status ValidateCorpusValue(const value_type&) const { + return absl::OkStatus(); // Nothing to validate. } auto GetPrinter() const { return IntegralPrinter{}; } @@ -178,8 +179,8 @@ } } - bool ValidateCorpusValue(const value_type&) const { - return true; // Nothing to validate. + absl::Status ValidateCorpusValue(const value_type&) const { + return absl::OkStatus(); // Nothing to validate. } auto GetPrinter() const { return IntegralPrinter{}; } @@ -217,8 +218,8 @@ val = std::byte{u8}; } - bool ValidateCorpusValue(const corpus_type&) const { - return true; // Nothing to validate. + absl::Status ValidateCorpusValue(const corpus_type&) const { + return absl::OkStatus(); // Nothing to validate. } auto GetPrinter() const { return IntegralPrinter{}; } @@ -270,8 +271,8 @@ } while (val == prev || (std::isnan(prev) && std::isnan(val))); } - bool ValidateCorpusValue(const value_type&) const { - return true; // Nothing to validate. + absl::Status ValidateCorpusValue(const value_type&) const { + return absl::OkStatus(); // Nothing to validate. } auto GetPrinter() const { return FloatingPrinter{}; } @@ -341,8 +342,8 @@ return IRObject::FromCorpus(v); } - bool ValidateCorpusValue(const corpus_type&) const { - return true; // Nothing to validate. + absl::Status ValidateCorpusValue(const corpus_type&) const { + return absl::OkStatus(); // Nothing to validate. } private:
diff --git a/fuzztest/internal/domains/bit_flag_combination_of_impl.h b/fuzztest/internal/domains/bit_flag_combination_of_impl.h index 0198668..73270a4 100644 --- a/fuzztest/internal/domains/bit_flag_combination_of_impl.h +++ b/fuzztest/internal/domains/bit_flag_combination_of_impl.h
@@ -63,7 +63,9 @@ } } - bool ValidateCorpusValue(const value_type&) const { return true; } + absl::Status ValidateCorpusValue(const value_type&) const { + return absl::OkStatus(); + } auto GetPrinter() const { return AutodetectTypePrinter<T>(); }
diff --git a/fuzztest/internal/domains/container_of_impl.h b/fuzztest/internal/domains/container_of_impl.h index 878284f..8e94f50 100644 --- a/fuzztest/internal/domains/container_of_impl.h +++ b/fuzztest/internal/domains/container_of_impl.h
@@ -26,6 +26,7 @@ #include "absl/random/bit_gen_ref.h" #include "absl/random/distributions.h" +#include "absl/status/status.h" #include "absl/strings/str_format.h" #include "absl/types/span.h" #include "./fuzztest/internal/coverage.h" @@ -34,6 +35,7 @@ #include "./fuzztest/internal/logging.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/table_of_recent_compares.h" #include "./fuzztest/internal/type_support.h" @@ -303,18 +305,27 @@ } } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { // Check size. - if (corpus_value.size() < min_size() || corpus_value.size() > max_size()) { - return false; + if (corpus_value.size() < min_size()) { + return absl::InvalidArgumentError(absl::StrCat( + "Invalid size: ", corpus_value.size(), ". Min size: ", min_size())); + } + if (corpus_value.size() > max_size()) { + return absl::InvalidArgumentError(absl::StrCat( + "Invalid size: ", corpus_value.size(), ". Max size: ", max_size())); } // Check elements. + int i = 0; for (const auto& elem : corpus_value) { - if (!inner_.ValidateCorpusValue(elem)) { - return false; + const absl::Status s = inner_.ValidateCorpusValue(elem); + if (!s.ok()) { + return Prefix(s, + absl::StrCat("Invalid value in container at index ", i)); } + i++; } - return true; + return absl::OkStatus(); } InnerDomainT Inner() const { return inner_; }
diff --git a/fuzztest/internal/domains/domain_base.h b/fuzztest/internal/domains/domain_base.h index 2c0234d..eb6be00 100644 --- a/fuzztest/internal/domains/domain_base.h +++ b/fuzztest/internal/domains/domain_base.h
@@ -27,6 +27,7 @@ #include "absl/random/bit_gen_ref.h" #include "absl/random/distributions.h" +#include "absl/status/status.h" #include "absl/strings/str_format.h" #include "./fuzztest/internal/any.h" #include "./fuzztest/internal/logging.h" @@ -70,7 +71,7 @@ const GenericDomainCorpusType& val) = 0; virtual std::optional<GenericDomainCorpusType> UntypedParseCorpus( const IRObject& obj) const = 0; - virtual bool UntypedValidateCorpusValue( + virtual absl::Status UntypedValidateCorpusValue( const GenericDomainCorpusType& corpus_value) const = 0; virtual IRObject UntypedSerializeCorpus( const GenericDomainCorpusType& v) const = 0; @@ -176,7 +177,7 @@ return derived().SerializeCorpus(v.template GetAs<CorpusType>()); } - bool UntypedValidateCorpusValue( + absl::Status UntypedValidateCorpusValue( const GenericDomainCorpusType& corpus_value) const final { return derived().ValidateCorpusValue(corpus_value.GetAs<CorpusType>()); } @@ -244,8 +245,8 @@ std::optional<CorpusType> corpus_value = derived().FromValue(seed); if (!corpus_value.has_value()) ReportBadSeedAndExit(seed); - bool valid = derived().ValidateCorpusValue(*corpus_value); - if (!valid) ReportBadSeedAndExit(seed); + absl::Status valid = derived().ValidateCorpusValue(*corpus_value); + if (!valid.ok()) ReportBadSeedAndExit(seed); seeds_.push_back(*std::move(corpus_value)); }
diff --git a/fuzztest/internal/domains/element_of_impl.h b/fuzztest/internal/domains/element_of_impl.h index 105fb5f..8e97755 100644 --- a/fuzztest/internal/domains/element_of_impl.h +++ b/fuzztest/internal/domains/element_of_impl.h
@@ -99,8 +99,11 @@ return IRObject::FromCorpus(v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { - return static_cast<size_t>(corpus_value) < values_.size(); + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { + if (static_cast<size_t>(corpus_value) < values_.size()) { + return absl::OkStatus(); + } + return absl::InvalidArgumentError("Invalid ElementOf() value"); } private:
diff --git a/fuzztest/internal/domains/filter_impl.h b/fuzztest/internal/domains/filter_impl.h index 1fc48cc..fc5e7f3 100644 --- a/fuzztest/internal/domains/filter_impl.h +++ b/fuzztest/internal/domains/filter_impl.h
@@ -23,6 +23,7 @@ #include "./fuzztest/internal/domains/domain_base.h" #include "./fuzztest/internal/logging.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -73,8 +74,10 @@ return inner_.SerializeCorpus(v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { - return predicate_(GetValue(corpus_value)); + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { + if (predicate_(GetValue(corpus_value))) return absl::OkStatus(); + return absl::InvalidArgumentError( + "Value does not match Filter() predicate."); } private:
diff --git a/fuzztest/internal/domains/flat_map_impl.h b/fuzztest/internal/domains/flat_map_impl.h index 4ab82cd..ea7a6ce 100644 --- a/fuzztest/internal/domains/flat_map_impl.h +++ b/fuzztest/internal/domains/flat_map_impl.h
@@ -26,6 +26,7 @@ #include "./fuzztest/internal/domains/serialization_helpers.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -138,15 +139,22 @@ return SerializeWithDomainTuple(domain, v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { // Check input values first. - bool input_values_valid = - ApplyIndex<sizeof...(InputDomain)>([&](auto... I) { - return (std::get<I>(input_domains_) - .ValidateCorpusValue(std::get<I + 1>(corpus_value)) && - ...); - }); - if (!input_values_valid) return false; + absl::Status input_values_validity = absl::OkStatus(); + ApplyIndex<sizeof...(InputDomain)>([&](auto... I) { + ( + [&] { + if (!input_values_validity.ok()) return; + const absl::Status s = + std::get<I>(input_domains_) + .ValidateCorpusValue(std::get<I + 1>(corpus_value)); + input_values_validity = + Prefix(s, "Invalid value for FlatMap()-ed domain"); + }(), + ...); + }); + if (!input_values_validity.ok()) return input_values_validity; // Check the output value. return GetOutputDomain(corpus_value) .ValidateCorpusValue(std::get<0>(corpus_value));
diff --git a/fuzztest/internal/domains/in_grammar_impl.h b/fuzztest/internal/domains/in_grammar_impl.h index 64f8912..ce4cd52 100644 --- a/fuzztest/internal/domains/in_grammar_impl.h +++ b/fuzztest/internal/domains/in_grammar_impl.h
@@ -673,11 +673,11 @@ return TopDomain::ParseCorpus(obj); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { // Validation is currently done during Parsing, and UserToCorpusValue() is // not supported yet. // TODO(lszekeres): Refactor so that validation happens here instead. - return true; + return absl::OkStatus(); } private:
diff --git a/fuzztest/internal/domains/in_range_impl.h b/fuzztest/internal/domains/in_range_impl.h index 9b9619f..4ab0f4d 100644 --- a/fuzztest/internal/domains/in_range_impl.h +++ b/fuzztest/internal/domains/in_range_impl.h
@@ -23,6 +23,7 @@ #include "absl/random/bit_gen_ref.h" #include "absl/random/distributions.h" +#include "absl/strings/str_cat.h" #include "./fuzztest/internal/coverage.h" #include "./fuzztest/internal/domains/domain_base.h" #include "./fuzztest/internal/domains/value_mutation_helpers.h" @@ -155,8 +156,19 @@ } while (val == prev); // Make sure Mutate really mutates. } - bool ValidateCorpusValue(const value_type& corpus_value) const { - return (min_ <= corpus_value && corpus_value <= max_); + absl::Status ValidateCorpusValue(const value_type& corpus_value) const { + if (min_ <= corpus_value && corpus_value <= max_) return absl::OkStatus(); + // We cannot just absl::StrCat() the error message, because it doesn't + // accept some types (like char). + std::string error_message; + absl::Format(&error_message, "The value "); + PrintValue(*this, corpus_value, &error_message, PrintMode::kSourceCode); + absl::Format(&error_message, " is not InRange("); + PrintValue(*this, min_, &error_message, PrintMode::kSourceCode); + absl::Format(&error_message, ", "); + PrintValue(*this, max_, &error_message, PrintMode::kSourceCode); + absl::Format(&error_message, ")"); + return absl::InvalidArgumentError(error_message); } auto GetPrinter() const {
diff --git a/fuzztest/internal/domains/in_regexp_impl.h b/fuzztest/internal/domains/in_regexp_impl.h index 82ef104..2940c93 100644 --- a/fuzztest/internal/domains/in_regexp_impl.h +++ b/fuzztest/internal/domains/in_regexp_impl.h
@@ -39,7 +39,7 @@ class InRegexpImpl : public DomainBase<InRegexpImpl, std::string, DFAPath> { public: explicit InRegexpImpl(std::string_view regex_str) - : dfa_(RegexpDFA::Create(regex_str)) {} + : regex_str_(regex_str), dfa_(RegexpDFA::Create(regex_str)) {} DFAPath Init(absl::BitGenRef prng) { if (auto seed = MaybeGetRandomSeed(prng)) return *seed; @@ -139,9 +139,11 @@ return obj; } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { // Check whether this is a valid path in the DFA. - return dfa_.DFAPathToString(corpus_value).has_value(); + if (dfa_.DFAPathToString(corpus_value).has_value()) return absl::OkStatus(); + return absl::InvalidArgumentError( + absl::StrCat("Invalid value for InRegexp(\"", regex_str_, "\")")); } private: @@ -230,6 +232,7 @@ } return false; } + std::string regex_str_; RegexpDFA dfa_; };
diff --git a/fuzztest/internal/domains/map_impl.h b/fuzztest/internal/domains/map_impl.h index e3ba937..3dcd080 100644 --- a/fuzztest/internal/domains/map_impl.h +++ b/fuzztest/internal/domains/map_impl.h
@@ -26,6 +26,7 @@ #include "./fuzztest/internal/domains/serialization_helpers.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -83,12 +84,19 @@ return SerializeWithDomainTuple(inner_, v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { - return ApplyIndex<sizeof...(Inner)>([&](auto... I) { - return ( - std::get<I>(inner_).ValidateCorpusValue(std::get<I>(corpus_value)) && + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status result = absl::OkStatus(); + ApplyIndex<sizeof...(Inner)>([&](auto... I) { + ( + [&] { + if (!result.ok()) return; + const absl::Status s = std::get<I>(inner_).ValidateCorpusValue( + std::get<I>(corpus_value)); + result = Prefix(s, "Invalid value for Map()-ed domain"); + }(), ...); }); + return result; } private: @@ -148,12 +156,21 @@ return SerializeWithDomainTuple(inner_, v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { - return ApplyIndex<sizeof...(Inner)>([&](auto... I) { - return ( - std::get<I>(inner_).ValidateCorpusValue(std::get<I>(corpus_value)) && + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status result = absl::OkStatus(); + ApplyIndex<sizeof...(Inner)>([&](auto... I) { + ( + [&] { + if (!result.ok()) return; + const absl::Status s = std::get<I>(inner_).ValidateCorpusValue( + std::get<I>(corpus_value)); + if (!s.ok()) { + result = Prefix(s, "Invalid value for BidiMap()-ed domain"); + } + }(), ...); }); + return result; } std::optional<corpus_type> FromValue(const value_type& v) const {
diff --git a/fuzztest/internal/domains/one_of_impl.h b/fuzztest/internal/domains/one_of_impl.h index 8db46ad..596a09c 100644 --- a/fuzztest/internal/domains/one_of_impl.h +++ b/fuzztest/internal/domains/one_of_impl.h
@@ -27,6 +27,7 @@ #include "./fuzztest/internal/domains/serialization_helpers.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -93,8 +94,9 @@ auto corpus_value = std::get<I>(domains_).FromValue(v); if (!corpus_value.has_value()) return false; - bool valid = std::get<I>(domains_).ValidateCorpusValue(*corpus_value); - if (!valid) return false; + const absl::Status valid = + std::get<I>(domains_).ValidateCorpusValue(*corpus_value); + if (!valid.ok()) return false; res.emplace(std::in_place_index<I>, *std::move(corpus_value)); return true; @@ -118,10 +120,11 @@ return SerializeWithDomainVariant(domains_, v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { return Switch<kNumDomains>(corpus_value.index(), [&](auto I) { - return std::get<I>(domains_).ValidateCorpusValue( - std::get<I>(corpus_value)); + const absl::Status s = + std::get<I>(domains_).ValidateCorpusValue(std::get<I>(corpus_value)); + return Prefix(s, "Invalid value for OneOf() domain"); }); }
diff --git a/fuzztest/internal/domains/optional_of_impl.h b/fuzztest/internal/domains/optional_of_impl.h index 12dc0ff..b592588 100644 --- a/fuzztest/internal/domains/optional_of_impl.h +++ b/fuzztest/internal/domains/optional_of_impl.h
@@ -27,6 +27,7 @@ #include "./fuzztest/internal/logging.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -119,15 +120,19 @@ return SerializeWithDomainOptional(inner_, v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { bool is_null = std::get_if<std::monostate>(&corpus_value); if (is_null) { - return policy_ != OptionalPolicy::kWithoutNull; - } else { - if (policy_ == OptionalPolicy::kAlwaysNull) return false; - // Validate inner object. - return inner_.ValidateCorpusValue(std::get<1>(corpus_value)); + if (policy_ == OptionalPolicy::kWithoutNull) { + return absl::InvalidArgumentError("Optional value must be set"); + } + return absl::OkStatus(); } + if (policy_ == OptionalPolicy::kAlwaysNull) { + return absl::InvalidArgumentError("Optional value must be null"); + } + // Validate inner object. + return inner_.ValidateCorpusValue(std::get<1>(corpus_value)); } OptionalOfImpl& SetAlwaysNull() {
diff --git a/fuzztest/internal/domains/protobuf_domain_impl.h b/fuzztest/internal/domains/protobuf_domain_impl.h index e452d96..1365114 100644 --- a/fuzztest/internal/domains/protobuf_domain_impl.h +++ b/fuzztest/internal/domains/protobuf_domain_impl.h
@@ -45,6 +45,7 @@ #include "./fuzztest/internal/logging.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/type_support.h" namespace google::protobuf { @@ -901,7 +902,7 @@ const ProtobufDomainUntypedImpl& self; // nullopt indicates that the field is not set. const std::optional<GenericDomainCorpusType>& corpus_value; - bool& out; + absl::Status& out; template <typename T> void VisitSingular(const FieldDescriptor* field) { @@ -909,8 +910,9 @@ corpus_value.has_value() ? *corpus_value : GetUnsetCorpusValue<T, /*is_repeated=*/false>(field); - out = self.GetSubDomain<T, /*is_repeated=*/false>(field) - .ValidateCorpusValue(value); + absl::Status s = self.GetSubDomain<T, /*is_repeated=*/false>(field) + .ValidateCorpusValue(value); + out = Prefix(s, absl::StrCat("Invalid value for field ", field->name())); } template <typename T> @@ -919,9 +921,10 @@ corpus_value.has_value() ? *corpus_value : GetUnsetCorpusValue<T, /*is_repeated=*/true>(field); - out = + absl::Status s = self.GetSubDomain<T, /*is_repeated=*/true>(field).ValidateCorpusValue( value); + out = Prefix(s, absl::StrCat("Invalid value for field ", field->name())); } private: @@ -941,7 +944,7 @@ } }; - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { for (int field_index = 0; field_index < prototype_.Get()->GetDescriptor()->field_count(); ++field_index) { @@ -951,12 +954,12 @@ auto inner_corpus_value = (field_number_value != corpus_value.end()) ? std::optional(field_number_value->second) : std::nullopt; - bool result; + absl::Status result; VisitProtobufField(field, ValidateVisitor{*this, inner_corpus_value, result}); - if (!result) return false; + if (!result.ok()) return result; } - return true; + return absl::OkStatus(); } auto GetPrinter() const { return ProtobufPrinter{}; } @@ -1531,7 +1534,7 @@ return inner_.SerializeCorpus(v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { return inner_.ValidateCorpusValue(corpus_value); } @@ -1996,8 +1999,8 @@ return ProtobufEnumPrinter<decltype(descriptor())>{descriptor()}; } - bool ValidateCorpusValue(const value_type&) const { - return true; // Any number is fine. + absl::Status ValidateCorpusValue(const value_type&) const { + return absl::OkStatus(); // Any number is fine. } private:
diff --git a/fuzztest/internal/domains/smart_pointer_of_impl.h b/fuzztest/internal/domains/smart_pointer_of_impl.h index 0e04edd..1245931 100644 --- a/fuzztest/internal/domains/smart_pointer_of_impl.h +++ b/fuzztest/internal/domains/smart_pointer_of_impl.h
@@ -24,6 +24,7 @@ #include "./fuzztest/internal/domains/domain_base.h" #include "./fuzztest/internal/domains/serialization_helpers.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -98,9 +99,11 @@ return SerializeWithDomainOptional(GetOrMakeInnerConst(), v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { - return (corpus_value.index() == 0) || - GetOrMakeInnerConst().ValidateCorpusValue(std::get<1>(corpus_value)); + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { + if (corpus_value.index() == 0) return absl::OkStatus(); + const absl::Status s = + GetOrMakeInnerConst().ValidateCorpusValue(std::get<1>(corpus_value)); + return Prefix(s, "Invalid value for smart pointer domain"); } private:
diff --git a/fuzztest/internal/domains/unique_elements_container_of_impl.h b/fuzztest/internal/domains/unique_elements_container_of_impl.h index 3db697f..35d573a 100644 --- a/fuzztest/internal/domains/unique_elements_container_of_impl.h +++ b/fuzztest/internal/domains/unique_elements_container_of_impl.h
@@ -85,7 +85,7 @@ return unique_domain_.SerializeCorpus(v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { return unique_domain_.ValidateCorpusValue(corpus_value); }
diff --git a/fuzztest/internal/domains/variant_of_impl.h b/fuzztest/internal/domains/variant_of_impl.h index a92cd4c..6b7ba70 100644 --- a/fuzztest/internal/domains/variant_of_impl.h +++ b/fuzztest/internal/domains/variant_of_impl.h
@@ -27,6 +27,7 @@ #include "./fuzztest/internal/domains/serialization_helpers.h" #include "./fuzztest/internal/meta.h" #include "./fuzztest/internal/serialization.h" +#include "./fuzztest/internal/status.h" #include "./fuzztest/internal/type_support.h" namespace fuzztest::internal { @@ -98,9 +99,11 @@ return SerializeWithDomainVariant(inner_, v); } - bool ValidateCorpusValue(const corpus_type& corpus_value) const { + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { return Switch<sizeof...(Inner)>(corpus_value.index(), [&](auto I) { - return std::get<I>(inner_).ValidateCorpusValue(std::get<I>(corpus_value)); + const absl::Status s = + std::get<I>(inner_).ValidateCorpusValue(std::get<I>(corpus_value)); + return Prefix(s, "Invalid value for variant domain"); }); }
diff --git a/fuzztest/internal/runtime.cc b/fuzztest/internal/runtime.cc index f1b21a9..5e54ce7 100644 --- a/fuzztest/internal/runtime.cc +++ b/fuzztest/internal/runtime.cc
@@ -32,6 +32,7 @@ #include "absl/random/bit_gen_ref.h" #include "absl/random/discrete_distribution.h" #include "absl/random/random.h" +#include "absl/status/status.h" #include "absl/strings/numbers.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" @@ -298,8 +299,9 @@ auto corpus_value = params_domain_->UntypedParseCorpus(*ir_value); if (!corpus_value) return std::nullopt; - bool valid = params_domain_->UntypedValidateCorpusValue(*corpus_value); - if (!valid) return std::nullopt; + absl::Status valid = + params_domain_->UntypedValidateCorpusValue(*corpus_value); + if (!valid.ok()) return std::nullopt; return corpus_value; }
diff --git a/fuzztest/internal/status.cc b/fuzztest/internal/status.cc new file mode 100644 index 0000000..3e5b92e --- /dev/null +++ b/fuzztest/internal/status.cc
@@ -0,0 +1,22 @@ +#include "./fuzztest/internal/status.h" + +#include "absl/strings/string_view.h" + +absl::Status SetMessage(const absl::Status& status, absl::string_view message) { + absl::Status result(status.code(), message); + status.ForEachPayload( + [&](absl::string_view type_url, const absl::Cord& payload) { + result.SetPayload(type_url, payload); + }); + return result; +} + +absl::Status Prefix(const absl::Status& status, absl::string_view prefix) { + if (status.ok() || prefix.empty()) return status; + return SetMessage(status, absl::StrCat(prefix, " >> ", status.message())); +} + +absl::Status Postfix(const absl::Status& status, absl::string_view postfix) { + if (status.ok() || postfix.empty()) return status; + return SetMessage(status, absl::StrCat(status.message(), " >> ", postfix)); +}
diff --git a/fuzztest/internal/status.h b/fuzztest/internal/status.h new file mode 100644 index 0000000..9cdc690 --- /dev/null +++ b/fuzztest/internal/status.h
@@ -0,0 +1,12 @@ +#ifndef FUZZTEST_FUZZTEST_INTERNAL_STATUS_H_ +#define FUZZTEST_FUZZTEST_INTERNAL_STATUS_H_ + +#include "absl/status/status.h" + +// Prefix status error message with `prefix`, if `status` is not OK. +absl::Status Prefix(const absl::Status& status, absl::string_view prefix); + +// Postfix status error message with `postfix`, if `status` is not OK. +absl::Status Postfix(const absl::Status& status, absl::string_view postfix); + +#endif // FUZZTEST_FUZZTEST_INTERNAL_STATUS_H_