| // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "build/build_config.h" |
| |
| #include <time.h> |
| |
| #if defined(OS_WIN) |
| #include <process.h> |
| #endif |
| |
| #include "base/time.h" |
| #include "base/platform_thread.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| // Test conversions to/from time_t and exploding/unexploding. |
| TEST(Time, TimeT) { |
| // C library time and exploded time. |
| time_t now_t_1 = time(NULL); |
| struct tm tms; |
| #if defined(OS_WIN) |
| localtime_s(&tms, &now_t_1); |
| #elif defined(OS_POSIX) |
| localtime_r(&now_t_1, &tms); |
| #endif |
| |
| // Convert to ours. |
| Time our_time_1 = Time::FromTimeT(now_t_1); |
| Time::Exploded exploded; |
| our_time_1.LocalExplode(&exploded); |
| |
| // This will test both our exploding and our time_t -> Time conversion. |
| EXPECT_EQ(tms.tm_year + 1900, exploded.year); |
| EXPECT_EQ(tms.tm_mon + 1, exploded.month); |
| EXPECT_EQ(tms.tm_mday, exploded.day_of_month); |
| EXPECT_EQ(tms.tm_hour, exploded.hour); |
| EXPECT_EQ(tms.tm_min, exploded.minute); |
| EXPECT_EQ(tms.tm_sec, exploded.second); |
| |
| // Convert exploded back to the time struct. |
| Time our_time_2 = Time::FromLocalExploded(exploded); |
| EXPECT_TRUE(our_time_1 == our_time_2); |
| |
| time_t now_t_2 = our_time_2.ToTimeT(); |
| EXPECT_EQ(now_t_1, now_t_2); |
| |
| EXPECT_EQ(10, Time().FromTimeT(10).ToTimeT()); |
| EXPECT_EQ(10.0, Time().FromTimeT(10).ToDoubleT()); |
| |
| // Conversions of 0 should stay 0. |
| EXPECT_EQ(0, Time().ToTimeT()); |
| EXPECT_EQ(0, Time::FromTimeT(0).ToInternalValue()); |
| } |
| |
| TEST(Time, ZeroIsSymmetric) { |
| Time zero_time(Time::FromTimeT(0)); |
| EXPECT_EQ(0, zero_time.ToTimeT()); |
| |
| EXPECT_EQ(0.0, zero_time.ToDoubleT()); |
| } |
| |
| TEST(Time, LocalExplode) { |
| Time a = Time::Now(); |
| Time::Exploded exploded; |
| a.LocalExplode(&exploded); |
| |
| Time b = Time::FromLocalExploded(exploded); |
| |
| // The exploded structure doesn't have microseconds, so the result will be |
| // rounded to the nearest millisecond. |
| EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1)); |
| } |
| |
| TEST(Time, UTCExplode) { |
| Time a = Time::Now(); |
| Time::Exploded exploded; |
| a.UTCExplode(&exploded); |
| |
| Time b = Time::FromUTCExploded(exploded); |
| EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1)); |
| } |
| |
| TEST(Time, LocalMidnight) { |
| Time::Exploded exploded; |
| Time::Now().LocalMidnight().LocalExplode(&exploded); |
| EXPECT_EQ(0, exploded.hour); |
| EXPECT_EQ(0, exploded.minute); |
| EXPECT_EQ(0, exploded.second); |
| EXPECT_EQ(0, exploded.millisecond); |
| } |
| |
| TEST(TimeTicks, Deltas) { |
| TimeTicks ticks_start = TimeTicks::Now(); |
| PlatformThread::Sleep(10); |
| TimeTicks ticks_stop = TimeTicks::Now(); |
| TimeDelta delta = ticks_stop - ticks_start; |
| EXPECT_GE(delta.InMilliseconds(), 10); |
| EXPECT_GE(delta.InMicroseconds(), 10000); |
| EXPECT_EQ(delta.InSeconds(), 0); |
| } |
| |
| TEST(TimeDelta, FromAndIn) { |
| EXPECT_TRUE(TimeDelta::FromDays(2) == TimeDelta::FromHours(48)); |
| EXPECT_TRUE(TimeDelta::FromHours(3) == TimeDelta::FromMinutes(180)); |
| EXPECT_TRUE(TimeDelta::FromMinutes(2) == TimeDelta::FromSeconds(120)); |
| EXPECT_TRUE(TimeDelta::FromSeconds(2) == TimeDelta::FromMilliseconds(2000)); |
| EXPECT_TRUE(TimeDelta::FromMilliseconds(2) == |
| TimeDelta::FromMicroseconds(2000)); |
| EXPECT_EQ(13, TimeDelta::FromDays(13).InDays()); |
| EXPECT_EQ(13, TimeDelta::FromHours(13).InHours()); |
| EXPECT_EQ(13, TimeDelta::FromMinutes(13).InMinutes()); |
| EXPECT_EQ(13, TimeDelta::FromSeconds(13).InSeconds()); |
| EXPECT_EQ(13.0, TimeDelta::FromSeconds(13).InSecondsF()); |
| EXPECT_EQ(13, TimeDelta::FromMilliseconds(13).InMilliseconds()); |
| EXPECT_EQ(13.0, TimeDelta::FromMilliseconds(13).InMillisecondsF()); |
| EXPECT_EQ(13, TimeDelta::FromMicroseconds(13).InMicroseconds()); |
| } |
| |
| #if defined(OS_WIN) |
| |
| // TODO(pinkerton): Need to find a way to mock this for non-windows. |
| |
| namespace { |
| |
| class MockTimeTicks : public TimeTicks { |
| public: |
| static int Ticker() { |
| return static_cast<int>(InterlockedIncrement(&ticker_)); |
| } |
| |
| static void InstallTicker() { |
| old_tick_function_ = tick_function_; |
| tick_function_ = reinterpret_cast<TickFunction>(&Ticker); |
| ticker_ = -5; |
| } |
| |
| static void UninstallTicker() { |
| tick_function_ = old_tick_function_; |
| } |
| |
| private: |
| static volatile LONG ticker_; |
| static TickFunction old_tick_function_; |
| }; |
| |
| volatile LONG MockTimeTicks::ticker_; |
| MockTimeTicks::TickFunction MockTimeTicks::old_tick_function_; |
| |
| HANDLE g_rollover_test_start; |
| |
| unsigned __stdcall RolloverTestThreadMain(void* param) { |
| int64 counter = reinterpret_cast<int64>(param); |
| DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE); |
| EXPECT_EQ(rv, WAIT_OBJECT_0); |
| |
| TimeTicks last = TimeTicks::Now(); |
| for (int index = 0; index < counter; index++) { |
| TimeTicks now = TimeTicks::Now(); |
| int64 milliseconds = (now - last).InMilliseconds(); |
| EXPECT_GT(milliseconds, 0); |
| EXPECT_LT(milliseconds, 250); |
| last = now; |
| } |
| return 0; |
| } |
| |
| } // namespace |
| |
| TEST(TimeTicks, Rollover) { |
| // The internal counter rolls over at ~49days. We'll use a mock |
| // timer to test this case. |
| // Basic test algorithm: |
| // 1) Set clock to rollover - N |
| // 2) Create N threads |
| // 3) Start the threads |
| // 4) Each thread loops through TimeTicks() N times |
| // 5) Each thread verifies integrity of result. |
| |
| const int kThreads = 8; |
| // Use int64 so we can cast into a void* without a compiler warning. |
| const int64 kChecks = 10; |
| |
| // It takes a lot of iterations to reproduce the bug! |
| // (See bug 1081395) |
| for (int loop = 0; loop < 4096; loop++) { |
| // Setup |
| MockTimeTicks::InstallTicker(); |
| g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0); |
| HANDLE threads[kThreads]; |
| |
| for (int index = 0; index < kThreads; index++) { |
| void* argument = reinterpret_cast<void*>(kChecks); |
| unsigned thread_id; |
| threads[index] = reinterpret_cast<HANDLE>( |
| _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0, |
| &thread_id)); |
| EXPECT_NE((HANDLE)NULL, threads[index]); |
| } |
| |
| // Start! |
| SetEvent(g_rollover_test_start); |
| |
| // Wait for threads to finish |
| for (int index = 0; index < kThreads; index++) { |
| DWORD rv = WaitForSingleObject(threads[index], INFINITE); |
| EXPECT_EQ(rv, WAIT_OBJECT_0); |
| } |
| |
| CloseHandle(g_rollover_test_start); |
| |
| // Teardown |
| MockTimeTicks::UninstallTicker(); |
| } |
| } |
| |
| #endif |
| |