blob: 9da577984140ed004df835344aa69da6184daeae [file] [log] [blame]
// 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