// 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 #if defined(OS_WIN) #include #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(InterlockedIncrement(&ticker_)); } static void InstallTicker() { old_tick_function_ = tick_function_; tick_function_ = reinterpret_cast(&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(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(kChecks); unsigned thread_id; threads[index] = reinterpret_cast( _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