summaryrefslogtreecommitdiffstats
path: root/widget/tests/gtest/TestTimeConverter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/tests/gtest/TestTimeConverter.cpp')
-rw-r--r--widget/tests/gtest/TestTimeConverter.cpp265
1 files changed, 265 insertions, 0 deletions
diff --git a/widget/tests/gtest/TestTimeConverter.cpp b/widget/tests/gtest/TestTimeConverter.cpp
new file mode 100644
index 0000000000..22cbc3f9e6
--- /dev/null
+++ b/widget/tests/gtest/TestTimeConverter.cpp
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/TimeStamp.h"
+#include "SystemTimeConverter.h"
+
+using mozilla::SystemTimeConverter;
+using mozilla::TimeDuration;
+using mozilla::TimeStamp;
+
+namespace {
+
+// This class provides a mock implementation of the CurrentTimeGetter template
+// type used in SystemTimeConverter. It can be constructed with a particular
+// Time and always returns that Time.
+template <typename Time>
+class MockCurrentTimeGetter {
+ public:
+ MockCurrentTimeGetter() : mTime(0) {}
+ explicit MockCurrentTimeGetter(Time aTime) : mTime(aTime) {}
+
+ // Methods needed for CurrentTimeGetter compatibility
+ Time GetCurrentTime() const { return mTime; }
+ void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow) {}
+
+ private:
+ Time mTime;
+};
+
+// This is another mock implementation of the CurrentTimeGetter template
+// type used in SystemTimeConverter, except this asserts that it will not be
+// used. i.e. it should only be used in calls to SystemTimeConverter that we
+// know will not invoke it.
+template <typename Time>
+class UnusedCurrentTimeGetter {
+ public:
+ Time GetCurrentTime() const {
+ EXPECT_TRUE(false);
+ return 0;
+ }
+
+ void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow) {
+ EXPECT_TRUE(false);
+ }
+};
+
+// This class provides a mock implementation of the TimeStampNowProvider
+// template type used in SystemTimeConverter. It also has other things in it
+// that allow the test to better control time for testing purposes.
+class MockTimeStamp {
+ public:
+ // This should generally be called at the start of every test function, as
+ // it will initialize this class's static fields to sane values. In particular
+ // it will initialize the baseline TimeStamp against which all other
+ // TimeStamps are compared.
+ static void Init() {
+ sBaseline = TimeStamp::Now();
+ sTimeStamp = sBaseline;
+ }
+
+ // Advance the timestamp returned by `MockTimeStamp::Now()`
+ static void Advance(double ms) {
+ sTimeStamp += TimeDuration::FromMilliseconds(ms);
+ }
+
+ // Returns the baseline TimeStamp, that is used as a fixed reference point
+ // in time against which other TimeStamps can be compared. This is needed
+ // because mozilla::TimeStamp itself doesn't provide any conversion to
+ // human-readable strings, and we need to convert it to a TimeDuration in
+ // order to get that. This baseline TimeStamp can be used to turn an
+ // arbitrary TimeStamp into a TimeDuration.
+ static TimeStamp Baseline() { return sBaseline; }
+
+ // This is the method needed for TimeStampNowProvider compatibility, and
+ // simulates `TimeStamp::Now()`
+ static TimeStamp Now() { return sTimeStamp; }
+
+ private:
+ static TimeStamp sTimeStamp;
+ static TimeStamp sBaseline;
+};
+
+TimeStamp MockTimeStamp::sTimeStamp;
+TimeStamp MockTimeStamp::sBaseline;
+
+// Could have platform-specific implementations of this using DWORD, guint32,
+// etc behind ifdefs. But this is sufficient for now.
+using GTestTime = uint32_t;
+using TimeConverter = SystemTimeConverter<GTestTime, MockTimeStamp>;
+
+} // namespace
+
+// Checks the expectation that the TimeStamp `ts` is exactly `ms` milliseconds
+// after the baseline timestamp. This is a macro so gtest still gives us useful
+// line numbers for failures.
+#define EXPECT_TS(ts, ms) \
+ EXPECT_EQ((ts)-MockTimeStamp::Baseline(), TimeDuration::FromMilliseconds(ms))
+
+#define EXPECT_TS_FUZZY(ts, ms) \
+ EXPECT_DOUBLE_EQ(((ts)-MockTimeStamp::Baseline()).ToMilliseconds(), ms)
+
+TEST(TimeConverter, SanityCheck)
+{
+ MockTimeStamp::Init();
+
+ MockCurrentTimeGetter timeGetter(10);
+ UnusedCurrentTimeGetter<GTestTime> unused;
+ TimeConverter converter;
+
+ // This call sets the reference time and timestamp
+ TimeStamp ts = converter.GetTimeStampFromSystemTime(10, timeGetter);
+ EXPECT_TS(ts, 0);
+
+ // Advance "TimeStamp::Now" by 10ms, use the same event time and OS time.
+ // Since the event time is the same as before, we expect to get back the
+ // same TimeStamp as before too, despite Now() changing.
+ MockTimeStamp::Advance(10);
+ ts = converter.GetTimeStampFromSystemTime(10, unused);
+ EXPECT_TS(ts, 0);
+
+ // Now let's use an event time 20ms after the old event. This will trigger
+ // forward skew detection and resync the TimeStamp for the new event to Now().
+ ts = converter.GetTimeStampFromSystemTime(30, unused);
+ EXPECT_TS(ts, 10);
+}
+
+TEST(TimeConverter, Overflow)
+{
+ // This tests wrapping time around the max value supported in the GTestTime
+ // type and ensuring it is handled properly.
+
+ MockTimeStamp::Init();
+
+ const GTestTime max = std::numeric_limits<GTestTime>::max();
+ const GTestTime min = std::numeric_limits<GTestTime>::min();
+ double fullRange = (double)max - (double)min;
+ double wrapPeriod = fullRange + 1.0;
+
+ GTestTime almostOverflowed = max - 100;
+ GTestTime overflowed = max + 100;
+ MockCurrentTimeGetter timeGetter(almostOverflowed);
+ UnusedCurrentTimeGetter<GTestTime> unused;
+ TimeConverter converter;
+
+ // Set reference time to 100ms before the overflow point
+ TimeStamp ts =
+ converter.GetTimeStampFromSystemTime(almostOverflowed, timeGetter);
+ EXPECT_TS(ts, 0);
+
+ // Advance everything by 200ms and verify we get back a TimeStamp 200ms from
+ // the baseline despite wrapping an overflow.
+ MockTimeStamp::Advance(200);
+ ts = converter.GetTimeStampFromSystemTime(overflowed, unused);
+ EXPECT_TS(ts, 200);
+
+ // Advance by another full wraparound of the time. This loses some precision
+ // so we have to do the FUZZY match
+ MockTimeStamp::Advance(wrapPeriod);
+ ts = converter.GetTimeStampFromSystemTime(overflowed, unused);
+ EXPECT_TS_FUZZY(ts, 200.0 + wrapPeriod);
+}
+
+TEST(TimeConverter, InvertedOverflow)
+{
+ // This tests time going from near the min value of GTestTime to the max
+ // value of GTestTime
+
+ MockTimeStamp::Init();
+
+ const GTestTime max = std::numeric_limits<GTestTime>::max();
+ const GTestTime min = std::numeric_limits<GTestTime>::min();
+ double fullRange = (double)max - (double)min;
+ double wrapPeriod = fullRange + 1.0;
+
+ GTestTime nearRangeMin = min + 100;
+ GTestTime nearRangeMax = max - 100;
+ double gap = (double)nearRangeMax - (double)nearRangeMin;
+
+ MockCurrentTimeGetter timeGetter(nearRangeMin);
+ UnusedCurrentTimeGetter<GTestTime> unused;
+ TimeConverter converter;
+
+ // Set reference time to value near min numeric limit
+ TimeStamp ts = converter.GetTimeStampFromSystemTime(nearRangeMin, timeGetter);
+ EXPECT_TS(ts, 0);
+
+ // Advance to value near max numeric limit
+ MockTimeStamp::Advance(gap);
+ ts = converter.GetTimeStampFromSystemTime(nearRangeMax, unused);
+ EXPECT_TS(ts, gap);
+
+ // Advance by another full wraparound of the time. This loses some precision
+ // so we have to do the FUZZY match
+ MockTimeStamp::Advance(wrapPeriod);
+ ts = converter.GetTimeStampFromSystemTime(nearRangeMax, unused);
+ EXPECT_TS_FUZZY(ts, gap + wrapPeriod);
+}
+
+TEST(TimeConverter, HalfRangeBoundary)
+{
+ MockTimeStamp::Init();
+
+ GTestTime max = std::numeric_limits<GTestTime>::max();
+ GTestTime min = std::numeric_limits<GTestTime>::min();
+ double fullRange = (double)max - (double)min;
+ double wrapPeriod = fullRange + 1.0;
+ GTestTime halfRange = (GTestTime)(fullRange / 2.0);
+ GTestTime halfWrapPeriod = (GTestTime)(wrapPeriod / 2.0);
+
+ TimeConverter converter;
+
+ GTestTime firstEvent = 10;
+ MockCurrentTimeGetter timeGetter(firstEvent);
+ UnusedCurrentTimeGetter<GTestTime> unused;
+
+ // Set reference time
+ TimeStamp ts = converter.GetTimeStampFromSystemTime(firstEvent, timeGetter);
+ EXPECT_TS(ts, 0);
+
+ // Advance event time by just under the half-period, to trigger about as big
+ // a forwards skew as we possibly can.
+ GTestTime secondEvent = firstEvent + (halfWrapPeriod - 1);
+ ts = converter.GetTimeStampFromSystemTime(secondEvent, unused);
+ EXPECT_TS(ts, 0);
+
+ // The above forwards skew will have reset the reference timestamp. Now
+ // advance Now time by just under the half-range, to trigger about as big
+ // a backwards skew as we possibly can.
+ MockTimeStamp::Advance(halfRange - 1);
+ ts = converter.GetTimeStampFromSystemTime(secondEvent, unused);
+ EXPECT_TS(ts, 0);
+}
+
+TEST(TimeConverter, FractionalMillisBug1626734)
+{
+ MockTimeStamp::Init();
+
+ TimeConverter converter;
+
+ GTestTime eventTime = 10;
+ MockCurrentTimeGetter timeGetter(eventTime);
+ UnusedCurrentTimeGetter<GTestTime> unused;
+
+ TimeStamp ts = converter.GetTimeStampFromSystemTime(eventTime, timeGetter);
+ EXPECT_TS(ts, 0);
+
+ MockTimeStamp::Advance(0.2);
+ ts = converter.GetTimeStampFromSystemTime(eventTime, unused);
+ EXPECT_TS(ts, 0);
+
+ MockTimeStamp::Advance(0.9);
+ TimeStamp ts2 = converter.GetTimeStampFromSystemTime(eventTime, unused);
+ EXPECT_TS(ts2, 0);
+
+ // Since ts2 came from a "future" call relative to ts, we expect ts2 to not
+ // be "before" ts. (i.e. time shouldn't go backwards, even by fractional
+ // milliseconds). This assertion is technically already implied by the
+ // EXPECT_TS checks above, but fixing this assertion is the end result that
+ // we wanted in bug 1626734 so it feels appropriate to recheck it explicitly.
+ EXPECT_TRUE(ts <= ts2);
+}