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