Arbitrary Status Domain PiperOrigin-RevId: 917993595
diff --git a/doc/domains-reference.md b/doc/domains-reference.md index 32ea226..f8421fd 100644 --- a/doc/domains-reference.md +++ b/doc/domains-reference.md
@@ -16,8 +16,9 @@ ### Arbitrary Domains -The `Arbitrary<T>()` domain is implemented for all native C++ types and for -protocol buffers. Specifically, for the following types: +The `Arbitrary<T>()` 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 @@ - [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<T>` or `std::vector<T>`, are supported as long as the inner types are. For example,
diff --git a/domain_tests/BUILD b/domain_tests/BUILD index ea40cd7..100fc80 100644 --- a/domain_tests/BUILD +++ b/domain_tests/BUILD
@@ -88,6 +88,7 @@ "@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 da7ef0f..ecf92b7 100644 --- a/domain_tests/CMakeLists.txt +++ b/domain_tests/CMakeLists.txt
@@ -68,6 +68,7 @@ "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 6b2829a..2af1c79 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 @@ Contains(Value(domain, absl::StatusCode::kInvalidArgument))); } +TEST(ArbitraryStatusTest, GeneratesOkAndError) { + auto domain = Arbitrary<absl::Status>(); + 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<absl::Status> domain = Arbitrary<absl::Status>().WithSeeds({seed}); + + EXPECT_THAT(GenerateInitialValues(domain, 1000), + Contains(Value(domain, seed))); +} + +TEST(ArbitraryStatusTest, FromValueAndGetValuePreserveCodeAndMessage) { + auto domain = Arbitrary<absl::Status>(); + 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 f5717cf..16bc2c6 100644 --- a/fuzztest/internal/domains/arbitrary_impl.h +++ b/fuzztest/internal/domains/arbitrary_impl.h
@@ -22,6 +22,7 @@ #include <limits> #include <memory> #include <optional> +#include <string> #include <string_view> #include <tuple> #include <type_traits> @@ -610,6 +611,33 @@ InRangeImpl<StatusCodeUnderlyingT>(0, 16)) {} }; +// Arbitrary for absl::Status. +// Note: This does not generate payloads. Payloads in seeds are ignored. +template <> +class ArbitraryImpl<absl::Status> + : public ReversibleMapImpl< + absl::Status (*)(absl::StatusCode, std::string), + std::optional<std::tuple<absl::StatusCode, std::string>> (*)( + absl::Status), + ArbitraryImpl<absl::StatusCode>, ArbitraryImpl<std::string>> { + public: + ArbitraryImpl() + : ReversibleMapImpl< + absl::Status (*)(absl::StatusCode, std::string), + std::optional<std::tuple<absl::StatusCode, std::string>> (*)( + absl::Status), + ArbitraryImpl<absl::StatusCode>, ArbitraryImpl<std::string>>( + [](absl::StatusCode code, std::string msg) { + return absl::Status(code, msg); + }, + +[](absl::Status status) + -> std::optional<std::tuple<absl::StatusCode, std::string>> { + return std::optional{ + std::tuple{status.code(), std::string(status.message())}}; + }, + ArbitraryImpl<absl::StatusCode>(), ArbitraryImpl<std::string>()) {} +}; + // Arbitrary for absl::BitGenRef. template <> class ArbitraryImpl<absl::BitGenRef>