Implement fuzztest version of MakeDuration() for Arbitrary<absl::Duration> domain.

Replace previous call of absl::time_internal::MakeDuration() with this new implementation. Now the Arbitrary<absl::Duration> domain does not depend on abseil internal functions any more.

PiperOrigin-RevId: 495881617
diff --git a/domain_tests/BUILD b/domain_tests/BUILD
index 9c93b2c..604af0d 100644
--- a/domain_tests/BUILD
+++ b/domain_tests/BUILD
@@ -57,6 +57,7 @@
         "@com_google_absl//absl/container:flat_hash_set",
         "@com_google_absl//absl/random",
         "@com_google_absl//absl/time",
+        "@com_google_fuzztest//fuzztest:absl_domain",
         "@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 412707f..ee72597 100644
--- a/domain_tests/arbitrary_domains_test.cc
+++ b/domain_tests/arbitrary_domains_test.cc
@@ -17,6 +17,7 @@
 #include <array>
 #include <cmath>
 #include <cstdint>
+#include <limits>
 #include <memory>
 #include <optional>
 #include <string>
@@ -34,6 +35,7 @@
 #include "absl/time/time.h"
 #include "./fuzztest/domain.h"
 #include "./domain_tests/domain_testing.h"
+#include "./fuzztest/internal/absl_domain.h"
 #include "./fuzztest/internal/domain.h"
 #include "./fuzztest/internal/serialization.h"
 #include "./fuzztest/internal/test_protobuf.pb.h"
@@ -598,6 +600,53 @@
   EXPECT_EQ(to, "aabcbcd");
 }
 
+// Note: this test is based on knowledge of internal representation of
+// absl::Duration and will fail if the internal representation changes.
+TEST(ArbitraryDurationTest, ValidatesAssumptionsAboutAbslDurationInternals) {
+  absl::Duration min_positive = absl::Nanoseconds(1) / 4;
+  absl::Duration max = absl::Seconds(std::numeric_limits<int64_t>::max()) +
+                       (absl::Seconds(1) - min_positive);
+
+  EXPECT_NE(absl::ZeroDuration(), min_positive);
+  EXPECT_EQ(absl::ZeroDuration(), (min_positive / 2));
+  EXPECT_NE(absl::InfiniteDuration(), max);
+  EXPECT_EQ(absl::InfiniteDuration(), max + min_positive);
+}
+
+TEST(ArbitraryDurationTest, ValidatesMakeDurationResults) {
+  EXPECT_EQ(internal::MakeDuration(0, 0), absl::ZeroDuration());
+  EXPECT_EQ(internal::MakeDuration(0, 1), absl::Nanoseconds(0.25));
+  EXPECT_EQ(internal::MakeDuration(0, 400'000), absl::Microseconds(100));
+  EXPECT_EQ(internal::MakeDuration(1, 500'000'000), absl::Seconds(1.125));
+  EXPECT_EQ(internal::MakeDuration(-50, 30), absl::Seconds(-49.9999999925));
+  EXPECT_EQ(internal::MakeDuration(-1, 3'999'999'999u),
+            absl::Nanoseconds(-0.25));
+  EXPECT_EQ(internal::MakeDuration(-2, 3'999'999'999u),
+            absl::Seconds(-1.00000000025));
+}
+
+TEST(ArbitraryDurationTest, ValidatesGetSecondsResults) {
+  EXPECT_EQ(internal::GetSeconds(internal::MakeDuration(10, 20)), 10);
+  EXPECT_EQ(internal::GetSeconds(internal::MakeDuration(-50, 30)), -50);
+  EXPECT_EQ(internal::GetSeconds(internal::MakeDuration(
+                std::numeric_limits<int64_t>::min(), 10)),
+            std::numeric_limits<int64_t>::min());
+  EXPECT_EQ(internal::GetSeconds(internal::MakeDuration(
+                std::numeric_limits<int64_t>::max(), 10)),
+            std::numeric_limits<int64_t>::max());
+}
+
+TEST(ArbitraryDurationTest, ValidatesGetTicksResults) {
+  EXPECT_EQ(internal::GetTicks(internal::MakeDuration(100, 200)), 200);
+  EXPECT_EQ(internal::GetTicks(internal::MakeDuration(-100, 200)), 200);
+  EXPECT_EQ(internal::GetTicks(internal::MakeDuration(
+                std::numeric_limits<int64_t>::min(), 3'999'999'999u)),
+            3'999'999'999u);
+  EXPECT_EQ(internal::GetTicks(internal::MakeDuration(
+                std::numeric_limits<int64_t>::max(), 3'999'999'999u)),
+            3'999'999'999u);
+}
+
 TEST(ArbitraryDurationTest, GeneratesAllTypesOfValues) {
   enum class DurationType {
     kInfinity,
@@ -630,12 +679,12 @@
 }
 
 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;
+  auto [secs, ticks] = internal::GetSecondsAndTicks(d);
+  if (secs == std::numeric_limits<int64_t>::min()) {
+    return static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1 +
+           ticks;
   }
-  return static_cast<uint64_t>(std::abs(hi)) + lo;
+  return static_cast<uint64_t>(std::abs(secs)) + ticks;
 }
 
 TEST(ArbitraryDurationTest, ShrinksCorrectly) {
diff --git a/fuzztest/BUILD b/fuzztest/BUILD
index d6099a3..6dbe207 100644
--- a/fuzztest/BUILD
+++ b/fuzztest/BUILD
@@ -23,6 +23,15 @@
 exports_files(["LICENSE"])
 
 cc_library(
+    name = "absl_domain",
+    hdrs = ["internal/absl_domain.h"],
+    deps = [
+        ":logging",
+        "@com_google_absl//absl/time",
+    ],
+)
+
+cc_library(
     name = "compatibility_mode",
     srcs = ["internal/compatibility_mode.cc"],
     hdrs = ["internal/compatibility_mode.h"],
@@ -79,6 +88,7 @@
         "internal/protobuf_domain.h",
     ],
     deps = [
+        ":absl_domain",
         ":coverage",
         ":logging",
         ":meta",
@@ -392,6 +402,7 @@
     srcs = ["internal/type_support.cc"],
     hdrs = ["internal/type_support.h"],
     deps = [
+        ":absl_domain",
         ":meta",
         "@com_google_absl//absl/debugging:symbolize",
         "@com_google_absl//absl/strings",
diff --git a/fuzztest/domain.h b/fuzztest/domain.h
index 3adabbe..904e966 100644
--- a/fuzztest/domain.h
+++ b/fuzztest/domain.h
@@ -40,6 +40,7 @@
 #include "absl/strings/str_format.h"
 #include "absl/time/time.h"
 #include "absl/types/span.h"
+#include "./fuzztest/internal/absl_domain.h"
 #include "./fuzztest/internal/domain.h"
 #include "./fuzztest/internal/logging.h"
 #include "./fuzztest/internal/meta.h"
@@ -1145,9 +1146,9 @@
 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]
+      Map([](int64_t secs,
+             uint32_t ticks) { return internal::MakeDuration(secs, ticks); },
+          // ticks is 1/4 of a nanosecond and has a range of [0, 4B - 1]
           Arbitrary<int64_t>(), InRange(0u, 3'999'999'999u)));
 }
 
diff --git a/fuzztest/internal/absl_domain.h b/fuzztest/internal/absl_domain.h
new file mode 100644
index 0000000..fb99ab2
--- /dev/null
+++ b/fuzztest/internal/absl_domain.h
@@ -0,0 +1,66 @@
+// 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.
+
+#ifndef FUZZTEST_FUZZTEST_INTERNAL_ABSL_DOMAIN_H_
+#define FUZZTEST_FUZZTEST_INTERNAL_ABSL_DOMAIN_H_
+
+#include <cstdint>
+
+#include "absl/time/time.h"
+#include "./fuzztest/internal/logging.h"
+
+namespace fuzztest::internal {
+
+// Note: this implementation is based on knowledge of internal
+// representation of absl::Duration and will not cover all
+// possible arbitrary durations if the internal representation
+// changes.
+inline absl::Duration MakeDuration(int64_t secs, uint32_t ticks) {
+  // The granularity of a duration is as small as a quarter of a
+  // nanosecond.
+  FUZZTEST_INTERNAL_CHECK_PRECONDITION(ticks >= 0u && ticks <= 3'999'999'999u,
+                                       "Ticks should be in range [0, 4B - 1]!");
+  return absl::Seconds(secs) + (absl::Nanoseconds(1) / 4) * ticks;
+}
+
+// Note: duration `d` needs to be finite.
+inline std::pair<int64_t, uint32_t> GetSecondsAndTicks(absl::Duration d) {
+  absl::Duration rem;
+  int64_t secs = absl::IDivDuration(d, absl::Seconds(1), &rem);
+  int64_t ticks = (4 * rem) / absl::Nanoseconds(1);
+  if (ticks < 0) {
+    // It is impossible to have both a negative remainder and int64min seconds.
+    FUZZTEST_INTERNAL_CHECK(secs != std::numeric_limits<int64_t>::min(),
+                            "Seconds should not be int64 min!");
+    secs -= 1;
+    ticks += 4'000'000'000;
+  }
+  FUZZTEST_INTERNAL_CHECK(0 <= ticks && ticks < 4'000'000'000,
+                          "Ticks should be in range [0, 4B - 1]!");
+  return {secs, static_cast<uint32_t>(ticks)};
+}
+
+// Note: duration `d` needs to be finite.
+inline int64_t GetSeconds(absl::Duration d) {
+  return GetSecondsAndTicks(d).first;
+}
+
+// Note: duration `d` needs to be finite.
+inline uint32_t GetTicks(absl::Duration d) {
+  return GetSecondsAndTicks(d).second;
+}
+
+}  // namespace fuzztest::internal
+
+#endif  // FUZZTEST_FUZZTEST_INTERNAL_ABSL_DOMAIN_H_
diff --git a/fuzztest/internal/type_support.h b/fuzztest/internal/type_support.h
index e8a47df..090698f 100644
--- a/fuzztest/internal/type_support.h
+++ b/fuzztest/internal/type_support.h
@@ -32,6 +32,7 @@
 #include "absl/strings/string_view.h"
 #include "absl/strings/strip.h"
 #include "absl/time/time.h"
+#include "./fuzztest/internal/absl_domain.h"
 #include "./fuzztest/internal/meta.h"
 
 namespace fuzztest::internal {
@@ -526,17 +527,17 @@
         } 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);
+          uint32_t ticks = GetTicks(duration);
+          int64_t secs = GetSeconds(duration);
+          if (ticks == 0) {
+            absl::Format(out, "absl::Seconds(%d)", secs);
+          } else if (ticks % 4 == 0) {
+            absl::Format(out, "absl::Seconds(%d) + absl::Nanoseconds(%u)", secs,
+                         ticks / 4);
           } else {
             absl::Format(out,
                          "absl::Seconds(%d) + (absl::Nanoseconds(1) / 4) * %u",
-                         rep_hi, rep_lo);
+                         secs, ticks);
           }
         }
         break;