Add implementation of Arbitrary<absl::Duration>() domain.

Write tests to check if all types of values are generated and if the domain shrinks correctly.
Add DurationPrinter and a test to properly print out the source code mode and the human readable mode of a Duration.

PiperOrigin-RevId: 492351452
diff --git a/domain_tests/BUILD b/domain_tests/BUILD
index 8e01a31..9c93b2c 100644
--- a/domain_tests/BUILD
+++ b/domain_tests/BUILD
@@ -56,6 +56,7 @@
         ":domain_testing",
         "@com_google_absl//absl/container:flat_hash_set",
         "@com_google_absl//absl/random",
+        "@com_google_absl//absl/time",
         "@com_google_fuzztest//fuzztest:domain",
         "@com_google_fuzztest//fuzztest:serialization",
         "@com_google_fuzztest//fuzztest:test_protobuf_cc_proto",
diff --git a/domain_tests/arbitrary_domains_test.cc b/domain_tests/arbitrary_domains_test.cc
index 572fa0c..45eeba0 100644
--- a/domain_tests/arbitrary_domains_test.cc
+++ b/domain_tests/arbitrary_domains_test.cc
@@ -15,6 +15,8 @@
 // Tests of Arbitrary<T> domains.
 
 #include <array>
+#include <cmath>
+#include <cstdint>
 #include <memory>
 #include <optional>
 #include <string>
@@ -29,6 +31,7 @@
 #include "gtest/gtest.h"
 #include "absl/container/flat_hash_set.h"
 #include "absl/random/random.h"
+#include "absl/time/time.h"
 #include "./fuzztest/domain.h"
 #include "./domain_tests/domain_testing.h"
 #include "./fuzztest/internal/domain.h"
@@ -41,6 +44,7 @@
 
 using ::testing::Each;
 using ::testing::ElementsAre;
+using ::testing::IsEmpty;
 using ::testing::SizeIs;
 using ::testing::UnorderedElementsAre;
 
@@ -594,5 +598,75 @@
   EXPECT_EQ(to, "aabcbcd");
 }
 
+TEST(ArbitraryDurationTest, GeneratesAllTypesOfValues) {
+  enum class DurationType {
+    kInfinity,
+    kMinusInfinity,
+    kZero,
+    kNegative,
+    kPositive
+  };
+
+  absl::BitGen bitgen;
+  absl::flat_hash_set<DurationType> to_find = {
+      DurationType::kInfinity, DurationType::kMinusInfinity,
+      DurationType::kZero, DurationType::kNegative, DurationType::kPositive};
+  for (int i = 0; i < 1000 && !to_find.empty(); ++i) {
+    auto domain = Arbitrary<absl::Duration>();
+    Value val(domain, bitgen);
+    if (val.user_value == absl::InfiniteDuration()) {
+      to_find.erase(DurationType::kInfinity);
+    } else if (val.user_value == -absl::InfiniteDuration()) {
+      to_find.erase(DurationType::kMinusInfinity);
+    } else if (val.user_value == absl::ZeroDuration()) {
+      to_find.erase(DurationType::kZero);
+    } else if (val.user_value < absl::ZeroDuration()) {
+      to_find.erase(DurationType::kNegative);
+    } else if (val.user_value > absl::ZeroDuration()) {
+      to_find.erase(DurationType::kPositive);
+    }
+  }
+  EXPECT_THAT(to_find, IsEmpty());
+}
+
+uint64_t AbsoluteValueOf(absl::Duration d) {
+  int64_t hi = absl::time_internal::GetRepHi(d);
+  uint32_t lo = absl::time_internal::GetRepLo(d);
+  if (hi == std::numeric_limits<int64_t>::min()) {
+    return static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1 + lo;
+  }
+  return static_cast<uint64_t>(std::abs(hi)) + lo;
+}
+
+TEST(ArbitraryDurationTest, ShrinksCorrectly) {
+  absl::BitGen bitgen;
+  auto domain = Arbitrary<absl::Duration>();
+  absl::flat_hash_set<Value<decltype(domain)>> values;
+  // Failure to generate 1000 non-special values in 10000 rounds is negligible
+  for (int i = 0; values.size() < 1000 && i < 10000; ++i) {
+    Value val(domain, bitgen);
+    values.insert(val);
+  }
+  ASSERT_THAT(values, SizeIs(1000));
+
+  ASSERT_TRUE(TestShrink(
+                  domain, values,
+                  [](auto v) {
+                    return (v == absl::InfiniteDuration() ||
+                            v == -absl::InfiniteDuration() ||
+                            v == absl::ZeroDuration());
+                  },
+                  [](auto prev, auto next) {
+                    // For values other than (-)inf, next is closer to zero,
+                    // so the absolute value of next is less than that of prev
+                    return ((prev == absl::InfiniteDuration() &&
+                             next == absl::InfiniteDuration()) ||
+                            (prev == -absl::InfiniteDuration() &&
+                             next == -absl::InfiniteDuration()) ||
+                            AbsoluteValueOf(next) < AbsoluteValueOf(prev));
+                  })
+                  .ok());
+}
+
 }  // namespace
 }  // namespace fuzztest
diff --git a/fuzztest/BUILD b/fuzztest/BUILD
index 001846e..cc358c3 100644
--- a/fuzztest/BUILD
+++ b/fuzztest/BUILD
@@ -98,6 +98,7 @@
         "@com_google_absl//absl/strings",
         "@com_google_absl//absl/strings:str_format",
         "@com_google_absl//absl/synchronization",
+        "@com_google_absl//absl/time",
         "@com_google_absl//absl/types:span",
     ],
 )
@@ -370,6 +371,7 @@
         "@com_google_absl//absl/debugging:symbolize",
         "@com_google_absl//absl/strings",
         "@com_google_absl//absl/strings:str_format",
+        "@com_google_absl//absl/time",
     ],
 )
 
@@ -382,6 +384,7 @@
         ":type_support",
         "@com_google_absl//absl/strings",
         "@com_google_absl//absl/strings:str_format",
+        "@com_google_absl//absl/time",
         "@com_google_googletest//:gtest_main",
     ],
 )
diff --git a/fuzztest/domain.h b/fuzztest/domain.h
index 151a475..f8a7c8a 100644
--- a/fuzztest/domain.h
+++ b/fuzztest/domain.h
@@ -38,6 +38,7 @@
 #include "absl/container/flat_hash_map.h"
 #include "absl/random/bit_gen_ref.h"
 #include "absl/strings/str_format.h"
+#include "absl/time/time.h"
 #include "absl/types/span.h"
 #include "./fuzztest/internal/domain.h"
 #include "./fuzztest/internal/logging.h"
@@ -1133,6 +1134,23 @@
   return inner.WithMinSize(1);
 }
 
+// Arbitrary<absl::Duration>() represents any absl::Duration, a signed,
+// fixed-length span of time.
+//
+// Example usage:
+//
+//   Arbitrary<absl::Duration>()
+//
+template <>
+inline auto Arbitrary<absl::Duration>() {
+  return OneOf(
+      ElementOf({absl::InfiniteDuration(), -absl::InfiniteDuration()}),
+      Map([](int64_t hi,
+             uint32_t lo) { return absl::time_internal::MakeDuration(hi, lo); },
+          // lo stores quarters of a nanosecond and has a range of [0, 4B - 1]
+          Arbitrary<int64_t>(), InRange(0u, 3'999'999'999u)));
+}
+
 }  // namespace internal_no_adl
 
 // Inject the names from internal_no_adl into fuzztest, without allowing for
diff --git a/fuzztest/internal/type_support.h b/fuzztest/internal/type_support.h
index 6b3c347..2beef29 100644
--- a/fuzztest/internal/type_support.h
+++ b/fuzztest/internal/type_support.h
@@ -31,6 +31,7 @@
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
 #include "absl/strings/strip.h"
+#include "absl/time/time.h"
 #include "./fuzztest/internal/meta.h"
 
 namespace fuzztest::internal {
@@ -510,6 +511,39 @@
   }
 };
 
+struct DurationPrinter {
+  void PrintUserValue(const absl::Duration duration, RawSink out,
+                      PrintMode mode) {
+    switch (mode) {
+      case PrintMode::kHumanReadable:
+        absl::Format(out, "%s", absl::FormatDuration(duration));
+        break;
+      case PrintMode::kSourceCode:
+        if (duration == absl::InfiniteDuration()) {
+          absl::Format(out, "absl::InfiniteDuration()");
+        } else if (duration == -absl::InfiniteDuration()) {
+          absl::Format(out, "-absl::InfiniteDuration()");
+        } else if (duration == absl::ZeroDuration()) {
+          absl::Format(out, "absl::ZeroDuration()");
+        } else {
+          uint32_t rep_lo = absl::time_internal::GetRepLo(duration);
+          int64_t rep_hi = absl::time_internal::GetRepHi(duration);
+          if (rep_lo == 0) {
+            absl::Format(out, "absl::Seconds(%d)", rep_hi);
+          } else if (rep_lo % 4 == 0) {
+            absl::Format(out, "absl::Seconds(%d) + absl::Nanoseconds(%u)",
+                         rep_hi, rep_lo / 4);
+          } else {
+            absl::Format(out,
+                         "absl::Seconds(%d) + (absl::Nanoseconds(1) / 4) * %u",
+                         rep_hi, rep_lo);
+          }
+        }
+        break;
+    }
+  }
+};
+
 struct UnknownPrinter {
   template <typename T>
   void PrintUserValue(const T& v, RawSink out, PrintMode mode) {
@@ -542,6 +576,8 @@
     return ProtobufPrinter{};
   } else if constexpr (is_bindable_aggregate_v<T>) {
     return AutodetectAggregatePrinter{};
+  } else if constexpr (std::is_same_v<T, absl::Duration>) {
+    return DurationPrinter{};
   } else {
     return UnknownPrinter{};
   }
diff --git a/fuzztest/internal/type_support_test.cc b/fuzztest/internal/type_support_test.cc
index 5be606d..de8c242 100644
--- a/fuzztest/internal/type_support_test.cc
+++ b/fuzztest/internal/type_support_test.cc
@@ -37,6 +37,7 @@
 #include "gtest/gtest.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
+#include "absl/time/time.h"
 #include "./fuzztest/domain.h"
 #include "./fuzztest/internal/domain.h"
 #include "./fuzztest/internal/protobuf_domain.h"
@@ -402,6 +403,25 @@
               ElementsAre("value={1, {Foo, Bar}}", R"({1, {"Foo", "Bar"}})"));
 }
 
+TEST(DurationTest, Printer) {
+  EXPECT_THAT(TestPrintValue(absl::InfiniteDuration()),
+              ElementsAre("inf", "absl::InfiniteDuration()"));
+  EXPECT_THAT(TestPrintValue(-absl::InfiniteDuration()),
+              ElementsAre("-inf", "-absl::InfiniteDuration()"));
+  EXPECT_THAT(TestPrintValue(absl::ZeroDuration()),
+              ElementsAre("0", "absl::ZeroDuration()"));
+  EXPECT_THAT(TestPrintValue(absl::Seconds(1)),
+              ElementsAre("1s", "absl::Seconds(1)"));
+  EXPECT_THAT(TestPrintValue(absl::Milliseconds(1500)),
+              ElementsAre("1.5s",
+                          "absl::Seconds(1) + "
+                          "absl::Nanoseconds(500000000)"));
+  EXPECT_THAT(TestPrintValue(absl::Nanoseconds(-0.25)),
+              ElementsAre("-0.25ns",
+                          "absl::Seconds(-1) + "
+                          "(absl::Nanoseconds(1) / 4) * 3999999999"));
+}
+
 struct NonAggregateStructWithNoStream {
   NonAggregateStructWithNoStream() : i(1), nested("Foo", "Bar") {}
   int i;