diff --git a/doc/domains-reference.md b/doc/domains-reference.md index 32ea226f4..f8421fdd7 100644 --- a/doc/domains-reference.md +++ b/doc/domains-reference.md @@ -16,8 +16,9 @@ use out of the box. ### Arbitrary Domains -The `Arbitrary()` domain is implemented for all native C++ types and for -protocol buffers. Specifically, for the following types: +The `Arbitrary()` domain is implemented for all native C++ types, +protocol buffers, and some Abseil types. +Specifically, for the following types: - Boolean type: `bool`. - Character types: `char`, `signed char`, `unsigned char`. @@ -43,7 +44,7 @@ protocol buffers. Specifically, for the following types: - [Abseil time library types](https://abseil.io/docs/cpp/guides/time): `absl::Duration`, `absl::Time`. - [Abseil status types](https://abseil.io/docs/cpp/guides/status): - `absl::StatusCode`. + `absl::StatusCode`, `absl::Status` (without support for payloads). Composite or container types, like `std::optional` or `std::vector`, are supported as long as the inner types are. For example, diff --git a/domain_tests/BUILD b/domain_tests/BUILD index ea40cd7ee..100fc8068 100644 --- a/domain_tests/BUILD +++ b/domain_tests/BUILD @@ -88,6 +88,7 @@ cc_test( "@abseil-cpp//absl/random", "@abseil-cpp//absl/random:bit_gen_ref", "@abseil-cpp//absl/status", + "@abseil-cpp//absl/strings:cord", "@abseil-cpp//absl/time", "@com_google_fuzztest//fuzztest:domain_core", "@com_google_fuzztest//fuzztest/internal:serialization", diff --git a/domain_tests/CMakeLists.txt b/domain_tests/CMakeLists.txt index da7ef0f2f..ecf92b709 100644 --- a/domain_tests/CMakeLists.txt +++ b/domain_tests/CMakeLists.txt @@ -68,6 +68,7 @@ fuzztest_cc_test( "arbitrary_domains_test.cc" DEPS fuzztest::domain_testing + absl::cord absl::flat_hash_set absl::random_random absl::random_bit_gen_ref diff --git a/domain_tests/arbitrary_domains_test.cc b/domain_tests/arbitrary_domains_test.cc index 6b2829a8d..2af1c7979 100644 --- a/domain_tests/arbitrary_domains_test.cc +++ b/domain_tests/arbitrary_domains_test.cc @@ -34,6 +34,7 @@ #include "absl/random/bit_gen_ref.h" #include "absl/random/random.h" #include "absl/status/status.h" +#include "absl/strings/cord.h" #include "absl/time/time.h" #include "./fuzztest/domain_core.h" // IWYU pragma: keep #include "./domain_tests/domain_testing.h" @@ -665,5 +666,43 @@ TEST(ArbitraryStatusCodeTest, InitGeneratesSeeds) { Contains(Value(domain, absl::StatusCode::kInvalidArgument))); } +TEST(ArbitraryStatusTest, GeneratesOkAndError) { + auto domain = Arbitrary(); + absl::BitGen prng; + bool found_ok = false; + bool found_error = false; + + for (int i = 0; i < 100 && (!found_ok || !found_error); ++i) { + absl::Status s = domain.GetRandomValue(prng); + if (s.ok()) { + found_ok = true; + } else { + found_error = true; + } + } + + EXPECT_TRUE(found_ok); + EXPECT_TRUE(found_error); +} + +TEST(ArbitraryStatusTest, InitGeneratesSeeds) { + absl::Status seed = absl::InvalidArgumentError("seed message"); + Domain domain = Arbitrary().WithSeeds({seed}); + + EXPECT_THAT(GenerateInitialValues(domain, 1000), + Contains(Value(domain, seed))); +} + +TEST(ArbitraryStatusTest, FromValueAndGetValuePreserveCodeAndMessage) { + auto domain = Arbitrary(); + absl::Status status = absl::InvalidArgumentError("msg"); + status.SetPayload("some_url", absl::Cord("payload")); + auto corpus_val = domain.FromValue(status); + ASSERT_TRUE(corpus_val.has_value()); + absl::Status mapped_status = domain.GetValue(*corpus_val); + EXPECT_EQ(mapped_status.code(), status.code()); + EXPECT_EQ(mapped_status.message(), status.message()); +} + } // namespace } // namespace fuzztest diff --git a/fuzztest/internal/domains/arbitrary_impl.h b/fuzztest/internal/domains/arbitrary_impl.h index f5717cf60..16bc2c679 100644 --- a/fuzztest/internal/domains/arbitrary_impl.h +++ b/fuzztest/internal/domains/arbitrary_impl.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -610,6 +611,33 @@ class ArbitraryImpl InRangeImpl(0, 16)) {} }; +// Arbitrary for absl::Status. +// Note: This does not generate payloads. Payloads in seeds are ignored. +template <> +class ArbitraryImpl + : public ReversibleMapImpl< + absl::Status (*)(absl::StatusCode, std::string), + std::optional> (*)( + absl::Status), + ArbitraryImpl, ArbitraryImpl> { + public: + ArbitraryImpl() + : ReversibleMapImpl< + absl::Status (*)(absl::StatusCode, std::string), + std::optional> (*)( + absl::Status), + ArbitraryImpl, ArbitraryImpl>( + [](absl::StatusCode code, std::string msg) { + return absl::Status(code, msg); + }, + +[](absl::Status status) + -> std::optional> { + return std::optional{ + std::tuple{status.code(), std::string(status.message())}}; + }, + ArbitraryImpl(), ArbitraryImpl()) {} +}; + // Arbitrary for absl::BitGenRef. template <> class ArbitraryImpl