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