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