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