initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 1 | // Copyright 2008, Google Inc. |
| 2 | // All rights reserved. |
| 3 | // |
| 4 | // Redistribution and use in source and binary forms, with or without |
| 5 | // modification, are permitted provided that the following conditions are |
| 6 | // met: |
| 7 | // |
| 8 | // * Redistributions of source code must retain the above copyright |
| 9 | // notice, this list of conditions and the following disclaimer. |
| 10 | // * Redistributions in binary form must reproduce the above |
| 11 | // copyright notice, this list of conditions and the following disclaimer |
| 12 | // in the documentation and/or other materials provided with the |
| 13 | // distribution. |
| 14 | // * Neither the name of Google Inc. nor the names of its |
| 15 | // contributors may be used to endorse or promote products derived from |
| 16 | // this software without specific prior written permission. |
| 17 | // |
| 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 30 | #include <time.h> |
| 31 | |
pinkerton@google.com | 75d7402 | 2008-08-14 07:55:45 +0900 | [diff] [blame^] | 32 | #if defined(OS_WIN) |
| 33 | #include <process.h> |
| 34 | #endif |
| 35 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 36 | #include "base/time.h" |
pinkerton@google.com | 75d7402 | 2008-08-14 07:55:45 +0900 | [diff] [blame^] | 37 | #include "base/platform_thread.h" |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 38 | #include "testing/gtest/include/gtest/gtest.h" |
| 39 | |
| 40 | // Test conversions to/from time_t and exploding/unexploding. |
| 41 | TEST(Time, TimeT) { |
| 42 | // C library time and exploded time. |
| 43 | time_t now_t_1 = time(NULL); |
| 44 | struct tm tms; |
pinkerton@google.com | 75d7402 | 2008-08-14 07:55:45 +0900 | [diff] [blame^] | 45 | #if defined(OS_WIN) |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 46 | localtime_s(&tms, &now_t_1); |
pinkerton@google.com | 75d7402 | 2008-08-14 07:55:45 +0900 | [diff] [blame^] | 47 | #elif defined(OS_POSIX) |
| 48 | localtime_r(&now_t_1, &tms); |
| 49 | #endif |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 50 | |
| 51 | // Convert to ours. |
| 52 | Time our_time_1 = Time::FromTimeT(now_t_1); |
| 53 | Time::Exploded exploded; |
| 54 | our_time_1.LocalExplode(&exploded); |
| 55 | |
| 56 | // This will test both our exploding and our time_t -> Time conversion. |
| 57 | EXPECT_EQ(tms.tm_year + 1900, exploded.year); |
| 58 | EXPECT_EQ(tms.tm_mon + 1, exploded.month); |
| 59 | EXPECT_EQ(tms.tm_mday, exploded.day_of_month); |
| 60 | EXPECT_EQ(tms.tm_hour, exploded.hour); |
| 61 | EXPECT_EQ(tms.tm_min, exploded.minute); |
| 62 | EXPECT_EQ(tms.tm_sec, exploded.second); |
| 63 | |
| 64 | // Convert exploded back to the time struct. |
| 65 | Time our_time_2 = Time::FromLocalExploded(exploded); |
| 66 | EXPECT_TRUE(our_time_1 == our_time_2); |
| 67 | |
| 68 | time_t now_t_2 = our_time_2.ToTimeT(); |
| 69 | EXPECT_EQ(now_t_1, now_t_2); |
| 70 | |
| 71 | // Conversions of 0 should stay 0. |
| 72 | EXPECT_EQ(0, Time().ToTimeT()); |
| 73 | EXPECT_EQ(0, Time::FromTimeT(0).ToInternalValue()); |
| 74 | } |
| 75 | |
| 76 | TEST(Time, ZeroIsSymmetric) { |
| 77 | Time zeroTime(Time::FromTimeT(0)); |
| 78 | EXPECT_EQ(0, zeroTime.ToTimeT()); |
| 79 | } |
| 80 | |
| 81 | TEST(Time, LocalExplode) { |
| 82 | Time a = Time::Now(); |
| 83 | Time::Exploded exploded; |
| 84 | a.LocalExplode(&exploded); |
| 85 | |
| 86 | Time b = Time::FromLocalExploded(exploded); |
| 87 | |
| 88 | // The exploded structure doesn't have microseconds, so the result will be |
| 89 | // rounded to the nearest millisecond. |
| 90 | EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1)); |
| 91 | } |
| 92 | |
| 93 | TEST(Time, UTCExplode) { |
| 94 | Time a = Time::Now(); |
| 95 | Time::Exploded exploded; |
| 96 | a.UTCExplode(&exploded); |
| 97 | |
| 98 | Time b = Time::FromUTCExploded(exploded); |
| 99 | EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1)); |
| 100 | } |
| 101 | |
| 102 | TEST(TimeTicks, Deltas) { |
| 103 | TimeTicks ticks_start = TimeTicks::Now(); |
pinkerton@google.com | 75d7402 | 2008-08-14 07:55:45 +0900 | [diff] [blame^] | 104 | PlatformThread::Sleep(10); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 105 | TimeTicks ticks_stop = TimeTicks::Now(); |
| 106 | TimeDelta delta = ticks_stop - ticks_start; |
| 107 | EXPECT_GE(delta.InMilliseconds(), 10); |
| 108 | EXPECT_GE(delta.InMicroseconds(), 10000); |
| 109 | EXPECT_EQ(delta.InSeconds(), 0); |
| 110 | } |
| 111 | |
pinkerton@google.com | 75d7402 | 2008-08-14 07:55:45 +0900 | [diff] [blame^] | 112 | #if defined(OS_WIN) |
| 113 | |
| 114 | // TODO(pinkerton): Need to find a way to mock this for non-windows. |
| 115 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 116 | namespace { |
| 117 | |
| 118 | class MockTimeTicks : public TimeTicks { |
| 119 | public: |
| 120 | static int Ticker() { |
| 121 | return static_cast<int>(InterlockedIncrement(&ticker_)); |
| 122 | } |
| 123 | |
| 124 | static void InstallTicker() { |
| 125 | old_tick_function_ = tick_function_; |
| 126 | tick_function_ = reinterpret_cast<TickFunction>(&Ticker); |
| 127 | ticker_ = -5; |
| 128 | } |
| 129 | |
| 130 | static void UninstallTicker() { |
| 131 | tick_function_ = old_tick_function_; |
| 132 | } |
| 133 | |
| 134 | private: |
| 135 | static volatile LONG ticker_; |
| 136 | static TickFunction old_tick_function_; |
| 137 | }; |
| 138 | |
| 139 | volatile LONG MockTimeTicks::ticker_; |
| 140 | MockTimeTicks::TickFunction MockTimeTicks::old_tick_function_; |
| 141 | |
| 142 | HANDLE g_rollover_test_start; |
| 143 | |
| 144 | unsigned __stdcall RolloverTestThreadMain(void* param) { |
| 145 | int64 counter = reinterpret_cast<int64>(param); |
| 146 | DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE); |
| 147 | EXPECT_EQ(rv, WAIT_OBJECT_0); |
| 148 | |
| 149 | TimeTicks last = TimeTicks::Now(); |
| 150 | for (int index = 0; index < counter; index++) { |
| 151 | TimeTicks now = TimeTicks::Now(); |
| 152 | int64 milliseconds = (now - last).InMilliseconds(); |
| 153 | EXPECT_GT(milliseconds, 0); |
| 154 | EXPECT_LT(milliseconds, 250); |
| 155 | last = now; |
| 156 | } |
| 157 | return 0; |
| 158 | } |
| 159 | |
| 160 | } // namespace |
| 161 | |
| 162 | TEST(TimeTicks, Rollover) { |
| 163 | // The internal counter rolls over at ~49days. We'll use a mock |
| 164 | // timer to test this case. |
| 165 | // Basic test algorithm: |
| 166 | // 1) Set clock to rollover - N |
| 167 | // 2) Create N threads |
| 168 | // 3) Start the threads |
| 169 | // 4) Each thread loops through TimeTicks() N times |
| 170 | // 5) Each thread verifies integrity of result. |
| 171 | |
| 172 | const int kThreads = 8; |
| 173 | // Use int64 so we can cast into a void* without a compiler warning. |
| 174 | const int64 kChecks = 10; |
| 175 | |
| 176 | // It takes a lot of iterations to reproduce the bug! |
| 177 | // (See bug 1081395) |
| 178 | for (int loop = 0; loop < 4096; loop++) { |
| 179 | // Setup |
| 180 | MockTimeTicks::InstallTicker(); |
| 181 | g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0); |
| 182 | HANDLE threads[kThreads]; |
| 183 | |
| 184 | for (int index = 0; index < kThreads; index++) { |
| 185 | void* argument = reinterpret_cast<void*>(kChecks); |
| 186 | unsigned thread_id; |
| 187 | threads[index] = reinterpret_cast<HANDLE>( |
| 188 | _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0, |
| 189 | &thread_id)); |
| 190 | EXPECT_NE((HANDLE)NULL, threads[index]); |
| 191 | } |
| 192 | |
| 193 | // Start! |
| 194 | SetEvent(g_rollover_test_start); |
| 195 | |
| 196 | // Wait for threads to finish |
| 197 | for (int index = 0; index < kThreads; index++) { |
| 198 | DWORD rv = WaitForSingleObject(threads[index], INFINITE); |
| 199 | EXPECT_EQ(rv, WAIT_OBJECT_0); |
| 200 | } |
| 201 | |
| 202 | CloseHandle(g_rollover_test_start); |
| 203 | |
| 204 | // Teardown |
| 205 | MockTimeTicks::UninstallTicker(); |
| 206 | } |
| 207 | } |
pinkerton@google.com | 75d7402 | 2008-08-14 07:55:45 +0900 | [diff] [blame^] | 208 | |
| 209 | #endif |