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