1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "gtest/gtest.h"
8 #include "mozilla/TimeStamp.h"
9 #include "SystemTimeConverter.h"
10 
11 using mozilla::SystemTimeConverter;
12 using mozilla::TimeDuration;
13 using mozilla::TimeStamp;
14 
15 namespace {
16 
17 // This class provides a mock implementation of the CurrentTimeGetter template
18 // type used in SystemTimeConverter. It can be constructed with a particular
19 // Time and always returns that Time.
20 template <typename Time>
21 class MockCurrentTimeGetter {
22  public:
MockCurrentTimeGetter()23   MockCurrentTimeGetter() : mTime(0) {}
MockCurrentTimeGetter(Time aTime)24   explicit MockCurrentTimeGetter(Time aTime) : mTime(aTime) {}
25 
26   // Methods needed for CurrentTimeGetter compatibility
GetCurrentTime() const27   Time GetCurrentTime() const { return mTime; }
GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp & aNow)28   void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow) {}
29 
30  private:
31   Time mTime;
32 };
33 
34 // This is another mock implementation of the CurrentTimeGetter template
35 // type used in SystemTimeConverter, except this asserts that it will not be
36 // used. i.e. it should only be used in calls to SystemTimeConverter that we
37 // know will not invoke it.
38 template <typename Time>
39 class UnusedCurrentTimeGetter {
40  public:
GetCurrentTime() const41   Time GetCurrentTime() const {
42     EXPECT_TRUE(false);
43     return 0;
44   }
45 
GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp & aNow)46   void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow) {
47     EXPECT_TRUE(false);
48   }
49 };
50 
51 // This class provides a mock implementation of the TimeStampNowProvider
52 // template type used in SystemTimeConverter. It also has other things in it
53 // that allow the test to better control time for testing purposes.
54 class MockTimeStamp {
55  public:
56   // This should generally be called at the start of every test function, as
57   // it will initialize this class's static fields to sane values. In particular
58   // it will initialize the baseline TimeStamp against which all other
59   // TimeStamps are compared.
Init()60   static void Init() {
61     sBaseline = TimeStamp::Now();
62     sTimeStamp = sBaseline;
63   }
64 
65   // Advance the timestamp returned by `MockTimeStamp::Now()`
Advance(double ms)66   static void Advance(double ms) {
67     sTimeStamp += TimeDuration::FromMilliseconds(ms);
68   }
69 
70   // Returns the baseline TimeStamp, that is used as a fixed reference point
71   // in time against which other TimeStamps can be compared. This is needed
72   // because mozilla::TimeStamp itself doesn't provide any conversion to
73   // human-readable strings, and we need to convert it to a TimeDuration in
74   // order to get that. This baseline TimeStamp can be used to turn an
75   // arbitrary TimeStamp into a TimeDuration.
Baseline()76   static TimeStamp Baseline() { return sBaseline; }
77 
78   // This is the method needed for TimeStampNowProvider compatibility, and
79   // simulates `TimeStamp::Now()`
Now()80   static TimeStamp Now() { return sTimeStamp; }
81 
82  private:
83   static TimeStamp sTimeStamp;
84   static TimeStamp sBaseline;
85 };
86 
87 TimeStamp MockTimeStamp::sTimeStamp;
88 TimeStamp MockTimeStamp::sBaseline;
89 
90 // Could have platform-specific implementations of this using DWORD, guint32,
91 // etc behind ifdefs. But this is sufficient for now.
92 using GTestTime = uint32_t;
93 using TimeConverter = SystemTimeConverter<GTestTime, MockTimeStamp>;
94 
95 }  // namespace
96 
97 // Checks the expectation that the TimeStamp `ts` is exactly `ms` milliseconds
98 // after the baseline timestamp. This is a macro so gtest still gives us useful
99 // line numbers for failures.
100 #define EXPECT_TS(ts, ms) \
101   EXPECT_EQ((ts)-MockTimeStamp::Baseline(), TimeDuration::FromMilliseconds(ms))
102 
103 #define EXPECT_TS_FUZZY(ts, ms) \
104   EXPECT_DOUBLE_EQ(((ts)-MockTimeStamp::Baseline()).ToMilliseconds(), ms)
105 
TEST(TimeConverter,SanityCheck)106 TEST(TimeConverter, SanityCheck)
107 {
108   MockTimeStamp::Init();
109 
110   MockCurrentTimeGetter timeGetter(10);
111   UnusedCurrentTimeGetter<GTestTime> unused;
112   TimeConverter converter;
113 
114   // This call sets the reference time and timestamp
115   TimeStamp ts = converter.GetTimeStampFromSystemTime(10, timeGetter);
116   EXPECT_TS(ts, 0);
117 
118   // Advance "TimeStamp::Now" by 10ms, use the same event time and OS time.
119   // Since the event time is the same as before, we expect to get back the
120   // same TimeStamp as before too, despite Now() changing.
121   MockTimeStamp::Advance(10);
122   ts = converter.GetTimeStampFromSystemTime(10, unused);
123   EXPECT_TS(ts, 0);
124 
125   // Now let's use an event time 20ms after the old event. This will trigger
126   // forward skew detection and resync the TimeStamp for the new event to Now().
127   ts = converter.GetTimeStampFromSystemTime(30, unused);
128   EXPECT_TS(ts, 10);
129 }
130 
TEST(TimeConverter,Overflow)131 TEST(TimeConverter, Overflow)
132 {
133   // This tests wrapping time around the max value supported in the GTestTime
134   // type and ensuring it is handled properly.
135 
136   MockTimeStamp::Init();
137 
138   const GTestTime max = std::numeric_limits<GTestTime>::max();
139   const GTestTime min = std::numeric_limits<GTestTime>::min();
140   double fullRange = (double)max - (double)min;
141   double wrapPeriod = fullRange + 1.0;
142 
143   GTestTime almostOverflowed = max - 100;
144   GTestTime overflowed = max + 100;
145   MockCurrentTimeGetter timeGetter(almostOverflowed);
146   UnusedCurrentTimeGetter<GTestTime> unused;
147   TimeConverter converter;
148 
149   // Set reference time to 100ms before the overflow point
150   TimeStamp ts =
151       converter.GetTimeStampFromSystemTime(almostOverflowed, timeGetter);
152   EXPECT_TS(ts, 0);
153 
154   // Advance everything by 200ms and verify we get back a TimeStamp 200ms from
155   // the baseline despite wrapping an overflow.
156   MockTimeStamp::Advance(200);
157   ts = converter.GetTimeStampFromSystemTime(overflowed, unused);
158   EXPECT_TS(ts, 200);
159 
160   // Advance by another full wraparound of the time. This loses some precision
161   // so we have to do the FUZZY match
162   MockTimeStamp::Advance(wrapPeriod);
163   ts = converter.GetTimeStampFromSystemTime(overflowed, unused);
164   EXPECT_TS_FUZZY(ts, 200.0 + wrapPeriod);
165 }
166 
TEST(TimeConverter,InvertedOverflow)167 TEST(TimeConverter, InvertedOverflow)
168 {
169   // This tests time going from near the min value of GTestTime to the max
170   // value of GTestTime
171 
172   MockTimeStamp::Init();
173 
174   const GTestTime max = std::numeric_limits<GTestTime>::max();
175   const GTestTime min = std::numeric_limits<GTestTime>::min();
176   double fullRange = (double)max - (double)min;
177   double wrapPeriod = fullRange + 1.0;
178 
179   GTestTime nearRangeMin = min + 100;
180   GTestTime nearRangeMax = max - 100;
181   double gap = (double)nearRangeMax - (double)nearRangeMin;
182 
183   MockCurrentTimeGetter timeGetter(nearRangeMin);
184   UnusedCurrentTimeGetter<GTestTime> unused;
185   TimeConverter converter;
186 
187   // Set reference time to value near min numeric limit
188   TimeStamp ts = converter.GetTimeStampFromSystemTime(nearRangeMin, timeGetter);
189   EXPECT_TS(ts, 0);
190 
191   // Advance to value near max numeric limit
192   MockTimeStamp::Advance(gap);
193   ts = converter.GetTimeStampFromSystemTime(nearRangeMax, unused);
194   EXPECT_TS(ts, gap);
195 
196   // Advance by another full wraparound of the time. This loses some precision
197   // so we have to do the FUZZY match
198   MockTimeStamp::Advance(wrapPeriod);
199   ts = converter.GetTimeStampFromSystemTime(nearRangeMax, unused);
200   EXPECT_TS_FUZZY(ts, gap + wrapPeriod);
201 }
202 
TEST(TimeConverter,HalfRangeBoundary)203 TEST(TimeConverter, HalfRangeBoundary)
204 {
205   MockTimeStamp::Init();
206 
207   GTestTime max = std::numeric_limits<GTestTime>::max();
208   GTestTime min = std::numeric_limits<GTestTime>::min();
209   double fullRange = (double)max - (double)min;
210   double wrapPeriod = fullRange + 1.0;
211   GTestTime halfRange = (GTestTime)(fullRange / 2.0);
212   GTestTime halfWrapPeriod = (GTestTime)(wrapPeriod / 2.0);
213 
214   TimeConverter converter;
215 
216   GTestTime firstEvent = 10;
217   MockCurrentTimeGetter timeGetter(firstEvent);
218   UnusedCurrentTimeGetter<GTestTime> unused;
219 
220   // Set reference time
221   TimeStamp ts = converter.GetTimeStampFromSystemTime(firstEvent, timeGetter);
222   EXPECT_TS(ts, 0);
223 
224   // Advance event time by just under the half-period, to trigger about as big
225   // a forwards skew as we possibly can.
226   GTestTime secondEvent = firstEvent + (halfWrapPeriod - 1);
227   ts = converter.GetTimeStampFromSystemTime(secondEvent, unused);
228   EXPECT_TS(ts, 0);
229 
230   // The above forwards skew will have reset the reference timestamp. Now
231   // advance Now time by just under the half-range, to trigger about as big
232   // a backwards skew as we possibly can.
233   MockTimeStamp::Advance(halfRange - 1);
234   ts = converter.GetTimeStampFromSystemTime(secondEvent, unused);
235   EXPECT_TS(ts, 0);
236 }
237 
TEST(TimeConverter,FractionalMillisBug1626734)238 TEST(TimeConverter, FractionalMillisBug1626734)
239 {
240   MockTimeStamp::Init();
241 
242   TimeConverter converter;
243 
244   GTestTime eventTime = 10;
245   MockCurrentTimeGetter timeGetter(eventTime);
246   UnusedCurrentTimeGetter<GTestTime> unused;
247 
248   TimeStamp ts = converter.GetTimeStampFromSystemTime(eventTime, timeGetter);
249   EXPECT_TS(ts, 0);
250 
251   MockTimeStamp::Advance(0.2);
252   ts = converter.GetTimeStampFromSystemTime(eventTime, unused);
253   EXPECT_TS(ts, 0);
254 
255   MockTimeStamp::Advance(0.9);
256   TimeStamp ts2 = converter.GetTimeStampFromSystemTime(eventTime, unused);
257   EXPECT_TS(ts2, 0);
258 
259   // Since ts2 came from a "future" call relative to ts, we expect ts2 to not
260   // be "before" ts. (i.e. time shouldn't go backwards, even by fractional
261   // milliseconds). This assertion is technically already implied by the
262   // EXPECT_TS checks above, but fixing this assertion is the end result that
263   // we wanted in bug 1626734 so it feels appropriate to recheck it explicitly.
264   EXPECT_TRUE(ts <= ts2);
265 }
266