time_win_unittest.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
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 <windows.h>
6#include <mmsystem.h>
7#include <process.h>
8
9#include "base/threading/platform_thread.h"
10#include "base/time.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13using base::Time;
14using base::TimeDelta;
15using base::TimeTicks;
16
17namespace {
18
19class MockTimeTicks : public TimeTicks {
20 public:
21  static DWORD Ticker() {
22    return static_cast<int>(InterlockedIncrement(&ticker_));
23  }
24
25  static void InstallTicker() {
26    old_tick_function_ = SetMockTickFunction(&Ticker);
27    ticker_ = -5;
28  }
29
30  static void UninstallTicker() {
31    SetMockTickFunction(old_tick_function_);
32  }
33
34 private:
35  static volatile LONG ticker_;
36  static TickFunctionType old_tick_function_;
37};
38
39volatile LONG MockTimeTicks::ticker_;
40MockTimeTicks::TickFunctionType MockTimeTicks::old_tick_function_;
41
42HANDLE g_rollover_test_start;
43
44unsigned __stdcall RolloverTestThreadMain(void* param) {
45  int64 counter = reinterpret_cast<int64>(param);
46  DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE);
47  EXPECT_EQ(rv, WAIT_OBJECT_0);
48
49  TimeTicks last = TimeTicks::Now();
50  for (int index = 0; index < counter; index++) {
51    TimeTicks now = TimeTicks::Now();
52    int64 milliseconds = (now - last).InMilliseconds();
53    // This is a tight loop; we could have looped faster than our
54    // measurements, so the time might be 0 millis.
55    EXPECT_GE(milliseconds, 0);
56    EXPECT_LT(milliseconds, 250);
57    last = now;
58  }
59  return 0;
60}
61
62}  // namespace
63
64TEST(TimeTicks, WinRollover) {
65  // The internal counter rolls over at ~49days.  We'll use a mock
66  // timer to test this case.
67  // Basic test algorithm:
68  //   1) Set clock to rollover - N
69  //   2) Create N threads
70  //   3) Start the threads
71  //   4) Each thread loops through TimeTicks() N times
72  //   5) Each thread verifies integrity of result.
73
74  const int kThreads = 8;
75  // Use int64 so we can cast into a void* without a compiler warning.
76  const int64 kChecks = 10;
77
78  // It takes a lot of iterations to reproduce the bug!
79  // (See bug 1081395)
80  for (int loop = 0; loop < 4096; loop++) {
81    // Setup
82    MockTimeTicks::InstallTicker();
83    g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0);
84    HANDLE threads[kThreads];
85
86    for (int index = 0; index < kThreads; index++) {
87      void* argument = reinterpret_cast<void*>(kChecks);
88      unsigned thread_id;
89      threads[index] = reinterpret_cast<HANDLE>(
90        _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0,
91          &thread_id));
92      EXPECT_NE((HANDLE)NULL, threads[index]);
93    }
94
95    // Start!
96    SetEvent(g_rollover_test_start);
97
98    // Wait for threads to finish
99    for (int index = 0; index < kThreads; index++) {
100      DWORD rv = WaitForSingleObject(threads[index], INFINITE);
101      EXPECT_EQ(rv, WAIT_OBJECT_0);
102    }
103
104    CloseHandle(g_rollover_test_start);
105
106    // Teardown
107    MockTimeTicks::UninstallTicker();
108  }
109}
110
111TEST(TimeTicks, SubMillisecondTimers) {
112  // HighResNow doesn't work on some systems.  Since the product still works
113  // even if it doesn't work, it makes this entire test questionable.
114  if (!TimeTicks::IsHighResClockWorking())
115    return;
116
117  const int kRetries = 1000;
118  bool saw_submillisecond_timer = false;
119
120  // Run kRetries attempts to see a sub-millisecond timer.
121  for (int index = 0; index < 1000; index++) {
122    TimeTicks last_time = TimeTicks::HighResNow();
123    TimeDelta delta;
124    // Spin until the clock has detected a change.
125    do {
126      delta = TimeTicks::HighResNow() - last_time;
127    } while (delta.InMicroseconds() == 0);
128    if (delta.InMicroseconds() < 1000) {
129      saw_submillisecond_timer = true;
130      break;
131    }
132  }
133  EXPECT_TRUE(saw_submillisecond_timer);
134}
135
136TEST(TimeTicks, TimeGetTimeCaps) {
137  // Test some basic assumptions that we expect about how timeGetDevCaps works.
138
139  TIMECAPS caps;
140  MMRESULT status = timeGetDevCaps(&caps, sizeof(caps));
141  EXPECT_EQ(TIMERR_NOERROR, status);
142  if (status != TIMERR_NOERROR) {
143    printf("Could not get timeGetDevCaps\n");
144    return;
145  }
146
147  EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
148  EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
149  EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
150  EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
151  printf("timeGetTime range is %d to %dms\n", caps.wPeriodMin,
152    caps.wPeriodMax);
153}
154
155TEST(TimeTicks, QueryPerformanceFrequency) {
156  // Test some basic assumptions that we expect about QPC.
157
158  LARGE_INTEGER frequency;
159  BOOL rv = QueryPerformanceFrequency(&frequency);
160  EXPECT_EQ(TRUE, rv);
161  EXPECT_GT(frequency.QuadPart, 1000000);  // Expect at least 1MHz
162  printf("QueryPerformanceFrequency is %5.2fMHz\n",
163    frequency.QuadPart / 1000000.0);
164}
165
166TEST(TimeTicks, TimerPerformance) {
167  // Verify that various timer mechanisms can always complete quickly.
168  // Note:  This is a somewhat arbitrary test.
169  const int kLoops = 10000;
170  // Due to the fact that these run on bbots, which are horribly slow,
171  // we can't really make any guarantees about minimum runtime.
172  // Really, we want these to finish in ~10ms, and that is generous.
173  const int kMaxTime = 35;  // Maximum acceptible milliseconds for test.
174
175  typedef TimeTicks (*TestFunc)();
176  struct TestCase {
177    TestFunc func;
178    char *description;
179  };
180  // Cheating a bit here:  assumes sizeof(TimeTicks) == sizeof(Time)
181  // in order to create a single test case list.
182  COMPILE_ASSERT(sizeof(TimeTicks) == sizeof(Time),
183                 test_only_works_with_same_sizes);
184  TestCase cases[] = {
185    { reinterpret_cast<TestFunc>(Time::Now), "Time::Now" },
186    { TimeTicks::Now, "TimeTicks::Now" },
187    { TimeTicks::HighResNow, "TimeTicks::HighResNow" },
188    { NULL, "" }
189  };
190
191  int test_case = 0;
192  while (cases[test_case].func) {
193    TimeTicks start = TimeTicks::HighResNow();
194    for (int index = 0; index < kLoops; index++)
195      cases[test_case].func();
196    TimeTicks stop = TimeTicks::HighResNow();
197    // Turning off the check for acceptible delays.  Without this check,
198    // the test really doesn't do much other than measure.  But the
199    // measurements are still useful for testing timers on various platforms.
200    // The reason to remove the check is because the tests run on many
201    // buildbots, some of which are VMs.  These machines can run horribly
202    // slow, and there is really no value for checking against a max timer.
203    //EXPECT_LT((stop - start).InMilliseconds(), kMaxTime);
204    printf("%s: %1.2fus per call\n", cases[test_case].description,
205      (stop - start).InMillisecondsF() * 1000 / kLoops);
206    test_case++;
207  }
208}
209
210TEST(TimeTicks, Drift) {
211  const int kIterations = 100;
212  int64 total_drift = 0;
213
214  for (int i = 0; i < kIterations; ++i) {
215    int64 drift_microseconds = TimeTicks::GetQPCDriftMicroseconds();
216
217    // Make sure the drift never exceeds our limit.
218    EXPECT_LT(drift_microseconds, 50000);
219
220    // Sleep for a few milliseconds (note that it means 1000 microseconds).
221    // If we check the drift too frequently, it's going to increase
222    // monotonically, making our measurement less realistic.
223    base::PlatformThread::Sleep((i % 2 == 0) ? 1 : 2);
224
225    total_drift += drift_microseconds;
226  }
227
228  // Sanity check. We expect some time drift to occur, especially across
229  // the number of iterations we do. However, if the QPC is disabled, this
230  // is not measuring anything (drift is zero in that case).
231  EXPECT_LT(0, total_drift);
232
233  printf("average time drift in microseconds: %lld\n",
234         total_drift / kIterations);
235}
236