xref: /aosp_15_r20/external/cronet/base/time/time_apple_unittest.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1// Copyright 2021 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/time/time.h"
6
7#include "base/test/gtest_util.h"
8#include "testing/gtest/include/gtest/gtest.h"
9
10namespace {
11
12class ScopedTimebase {
13 public:
14  explicit ScopedTimebase(mach_timebase_info_data_t timebase) {
15    orig_timebase_ = base::TimeTicks::SetMachTimebaseInfoForTesting(timebase);
16  }
17
18  ScopedTimebase(const ScopedTimebase&) = delete;
19
20  ScopedTimebase& operator=(const ScopedTimebase&) = delete;
21
22  ~ScopedTimebase() {
23    base::TimeTicks::SetMachTimebaseInfoForTesting(orig_timebase_);
24  }
25
26 private:
27  mach_timebase_info_data_t orig_timebase_;
28};
29
30mach_timebase_info_data_t kIntelTimebase = {1, 1};
31
32// A sample (not definitive) timebase for M1.
33mach_timebase_info_data_t kM1Timebase = {125, 3};
34
35}  // namespace
36
37namespace base {
38namespace {
39
40base::Time NoonOnDate(int year, int month, int day) {
41  const base::Time::Exploded exploded = {
42      .year = year, .month = month, .day_of_month = day, .hour = 12};
43  base::Time imploded;
44  CHECK(base::Time::FromUTCExploded(exploded, &imploded));
45  return imploded;
46}
47
48void CheckRoundTrip(int y, int m, int d) {
49  base::Time original = NoonOnDate(y, m, d);
50  base::Time roundtrip = Time::FromNSDate(original.ToNSDate());
51  EXPECT_EQ(original, roundtrip);
52}
53
54TEST(TimeMacTest, RoundTripNSDate) {
55  CheckRoundTrip(1911, 12, 14);
56  CheckRoundTrip(1924, 9, 28);
57  CheckRoundTrip(1926, 5, 12);
58  CheckRoundTrip(1969, 7, 24);
59}
60
61TEST(TimeMacTest, MachTimeToMicrosecondsIntelTimebase) {
62  ScopedTimebase timebase(kIntelTimebase);
63
64  // Perform the conversion.
65  uint64_t kArbitraryTicks = 59090101000;
66  TimeDelta result = TimeDelta::FromMachTime(kArbitraryTicks);
67
68  // With Intel the output should be the input.
69  EXPECT_EQ(Nanoseconds(kArbitraryTicks), result);
70}
71
72TEST(TimeMacTest, MachTimeToMicrosecondsM1Timebase) {
73  ScopedTimebase timebase(kM1Timebase);
74
75  // Use a tick count that's divisible by 3.
76  const uint64_t kArbitraryTicks = 92738127000;
77  TimeDelta result = TimeDelta::FromMachTime(kArbitraryTicks);
78
79  const uint64_t kExpectedResult =
80      kArbitraryTicks * kM1Timebase.numer / kM1Timebase.denom;
81  EXPECT_EQ(Nanoseconds(kExpectedResult), result);
82}
83
84// Tests MachTimeToMicroseconds when
85// mach_timebase_info_data_t.numer and mach_timebase_info_data_t.denom
86// are equal.
87TEST(TimeMacTest, MachTimeToMicrosecondsEqualTimebaseMembers) {
88  // These members would produce overflow but don't because
89  // MachTimeToMicroseconds should skip the timebase conversion
90  // when they're equal.
91  ScopedTimebase timebase({UINT_MAX, UINT_MAX});
92
93  uint64_t kArbitraryTicks = 175920053729;
94  TimeDelta result = TimeDelta::FromMachTime(kArbitraryTicks);
95
96  // With a unity timebase the output should be the input.
97  EXPECT_EQ(Nanoseconds(kArbitraryTicks), result);
98}
99
100TEST(TimeMacTest, MachTimeToMicrosecondsOverflowDetection) {
101  const uint32_t kArbitraryNumer = 1234567;
102  ScopedTimebase timebase({kArbitraryNumer, 1});
103
104  // Expect an overflow.
105  EXPECT_CHECK_DEATH(
106      TimeDelta::FromMachTime(std::numeric_limits<uint64_t>::max()));
107}
108
109// Tests that there's no overflow in MachTimeToMicroseconds even with
110// std::numeric_limits<uint64_t>::max() ticks on Intel.
111TEST(TimeMacTest, MachTimeToMicrosecondsNoOverflowIntel) {
112  ScopedTimebase timebase(kIntelTimebase);
113
114  // The incoming Mach time ticks are on the order of nanoseconds while the
115  // return result is microseconds. Even though we're passing in the largest
116  // tick count the result should be orders of magnitude smaller. On Intel the
117  // mapping from ticks to nanoseconds is 1:1 so we wouldn't ever expect an
118  // overflow when applying the timebase conversion.
119  TimeDelta::FromMachTime(std::numeric_limits<uint64_t>::max());
120}
121
122// Tests that there's no overflow in MachTimeToMicroseconds even with
123// std::numeric_limits<uint64_t>::max() ticks on M1.
124TEST(TimeMacTest, MachTimeToMicrosecondsNoOverflowM1) {
125  ScopedTimebase timebase(kM1Timebase);
126
127  // The incoming Mach time ticks are on the order of nanoseconds while the
128  // return result is microseconds. Even though we're passing in the largest
129  // tick count the result should be orders of magnitude smaller. Expect that
130  // FromMachTime(), when applying the timebase conversion, is smart enough to
131  // not multiply first and generate an overflow.
132  TimeDelta::FromMachTime(std::numeric_limits<uint64_t>::max());
133}
134
135// Tests that there's no underflow in MachTimeToMicroseconds on Intel.
136TEST(TimeMacTest, MachTimeToMicrosecondsNoUnderflowIntel) {
137  ScopedTimebase timebase(kIntelTimebase);
138
139  // On Intel the timebase conversion is 1:1, so min ticks is one microsecond
140  // worth of nanoseconds.
141  const uint64_t kMinimumTicks = base::Time::kNanosecondsPerMicrosecond;
142  const uint64_t kOneMicrosecond = 1;
143  EXPECT_EQ(kOneMicrosecond,
144            TimeDelta::FromMachTime(kMinimumTicks).InMicroseconds() * 1UL);
145
146  // If we have even one fewer tick (i.e. not enough ticks to constitute a full
147  // microsecond) the integer rounding should result in 0 microseconds.
148  const uint64_t kZeroMicroseconds = 0;
149  EXPECT_EQ(kZeroMicroseconds,
150            TimeDelta::FromMachTime(kMinimumTicks - 1).InMicroseconds() * 1UL);
151}
152
153// Tests that there's no underflow in MachTimeToMicroseconds for M1.
154TEST(TimeMacTest, MachTimeToMicrosecondsNoUnderflowM1) {
155  ScopedTimebase timebase(kM1Timebase);
156
157  // Microseconds is mach_time multiplied by kM1Timebase.numer /
158  // (kM1Timebase.denom * base::Time::kNanosecondsPerMicrosecond). Inverting
159  // that should be the minimum number of ticks to get a single microsecond in
160  // return. If we get zero it means an underflow in the conversion. For example
161  // if FromMachTime() first divides mach_time by kM1Timebase.denom *
162  // base::Time::kNanosecondsPerMicrosecond we'll get zero back.
163  const uint64_t kMinimumTicks =
164      (kM1Timebase.denom * base::Time::kNanosecondsPerMicrosecond) /
165      kM1Timebase.numer;
166  const uint64_t kOneMicrosecond = 1;
167  EXPECT_EQ(kOneMicrosecond,
168            TimeDelta::FromMachTime(kMinimumTicks).InMicroseconds() * 1UL);
169
170  // If we have even one fewer tick (i.e. not enough ticks to constitute a full
171  // microsecond) the integer rounding should result in 0 microseconds.
172  const uint64_t kZeroMicroseconds = 0;
173  EXPECT_EQ(kZeroMicroseconds,
174            TimeDelta::FromMachTime(kMinimumTicks - 1).InMicroseconds() * 1UL);
175}
176
177}  // namespace
178}  // namespace base
179