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
|
// 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
|