blob: e4b45ec4f3ab13c91eccfecafc3828b56ade9899 [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 "base/message_loop.h"
#include "base/task.h"
#include "base/timer.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::TimerComparison;
namespace {
class TimerTest : public testing::Test {};
// A base class timer task that sanity-checks timer functionality and counts
// the number of times it has run. Handles all message loop and memory
// management issues.
class TimerTask : public Task {
public:
// Runs all timers to completion. This returns only after all timers have
// finished firing.
static void RunTimers();
// Creates a new timer. If |repeating| is true, the timer will repeat 10
// times before terminating.
//
// All timers are managed on the message loop of the thread that calls this
// function the first time.
TimerTask(int delay, bool repeating);
virtual ~TimerTask();
int iterations() const { return iterations_; }
const Timer* timer() const { return timer_; }
// Resets the timer, if it exists.
void Reset();
// Task
virtual void Run();
protected:
// Shuts down the message loop if necessary.
static void QuitMessageLoop();
private:
static MessageLoop* message_loop() {
return MessageLoop::current();
}
static int timer_count_;
static bool loop_running_;
bool timer_running_;
int delay_;
TimeTicks start_ticks_;
int iterations_;
Timer* timer_;
};
// static
void TimerTask::RunTimers() {
if (timer_count_ && !loop_running_) {
loop_running_ = true;
message_loop()->Run();
}
}
TimerTask::TimerTask(int delay, bool repeating)
: timer_running_(false),
delay_(delay),
start_ticks_(TimeTicks::Now()),
iterations_(0),
timer_(NULL) {
Reset(); // This will just set up the variables to indicate we have a
// running timer.
timer_ = message_loop()->timer_manager()->StartTimer(delay, this, repeating);
}
TimerTask::~TimerTask() {
if (timer_) {
message_loop()->timer_manager()->StopTimer(timer_);
delete timer_;
}
if (timer_running_) {
timer_running_ = false;
if (--timer_count_ <= 0)
QuitMessageLoop();
}
}
void TimerTask::Reset() {
if (!timer_running_) {
timer_running_ = true;
++timer_count_;
}
if (timer_) {
start_ticks_ = TimeTicks::Now();
message_loop()->timer_manager()->ResetTimer(timer_);
}
}
void TimerTask::Run() {
++iterations_;
// Test that we fired on or after the delay, not before.
const TimeTicks ticks = TimeTicks::Now();
EXPECT_LE(delay_, (ticks - start_ticks_).InMilliseconds());
// Note: Add the delay rather than using the ticks recorded.
// Repeating timers have already started ticking before
// this callback; we pretend they started *now*, then
// it might seem like they fire early, when they do not.
start_ticks_ += TimeDelta::FromMilliseconds(delay_);
// If we're done running, shut down the message loop.
if (timer_->repeating() && (iterations_ < 10))
return; // Iterate 10 times before terminating.
message_loop()->timer_manager()->StopTimer(timer_);
timer_running_ = false;
if (--timer_count_ <= 0)
QuitMessageLoop();
}
// static
void TimerTask::QuitMessageLoop() {
if (loop_running_) {
message_loop()->Quit();
loop_running_ = false;
}
}
int TimerTask::timer_count_ = 0;
bool TimerTask::loop_running_ = false;
// A task that deletes itself when run.
class DeletingTask : public TimerTask {
public:
DeletingTask(int delay, bool repeating) : TimerTask(delay, repeating) { }
// Task
virtual void Run();
};
void DeletingTask::Run() {
delete this;
// Can't call TimerTask::Run() here, we've destroyed ourselves.
}
// A class that resets another TimerTask when run.
class ResettingTask : public TimerTask {
public:
ResettingTask(int delay, bool repeating, TimerTask* task)
: TimerTask(delay, repeating),
task_(task) {
}
virtual void Run();
private:
TimerTask* task_;
};
void ResettingTask::Run() {
task_->Reset();
TimerTask::Run();
}
// A class that quits the message loop when run.
class QuittingTask : public TimerTask {
public:
QuittingTask(int delay, bool repeating) : TimerTask(delay, repeating) { }
virtual void Run();
};
void QuittingTask::Run() {
QuitMessageLoop();
TimerTask::Run();
}
void RunTimerTest() {
// Make sure oneshot timers work correctly.
TimerTask task1(100, false);
TimerTask::RunTimers();
EXPECT_EQ(1, task1.iterations());
// Make sure repeating timers work correctly.
TimerTask task2(10, true);
TimerTask task3(100, true);
TimerTask::RunTimers();
EXPECT_EQ(10, task2.iterations());
EXPECT_EQ(10, task3.iterations());
}
//-----------------------------------------------------------------------------
// The timer test cases:
void RunTest_TimerComparison(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
// Make sure TimerComparison sorts correctly.
const TimerTask task1(10, false);
const Timer* timer1 = task1.timer();
const TimerTask task2(200, false);
const Timer* timer2 = task2.timer();
TimerComparison comparison;
EXPECT_FALSE(comparison(timer1, timer2));
EXPECT_TRUE(comparison(timer2, timer1));
}
void RunTest_BasicTimer(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
RunTimerTest();
}
void RunTest_BrokenTimer(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
// Simulate faulty early-firing timers. The tasks in RunTimerTest should
// nevertheless be invoked after their specified delays, regardless of when
// WM_TIMER fires.
TimerManager* manager = MessageLoop::current()->timer_manager();
manager->set_use_broken_delay(true);
RunTimerTest();
manager->set_use_broken_delay(false);
}
void RunTest_DeleteFromRun(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
// Make sure TimerManager correctly handles a Task that deletes itself when
// run.
new DeletingTask(50, true);
TimerTask timer_task(150, false);
new DeletingTask(250, true);
TimerTask::RunTimers();
EXPECT_EQ(1, timer_task.iterations());
}
void RunTest_Reset(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
// Make sure resetting a timer after it has fired works.
TimerTask timer_task1(250, false);
TimerTask timer_task2(100, true);
ResettingTask resetting_task1(600, false, &timer_task1);
TimerTask::RunTimers();
EXPECT_EQ(2, timer_task1.iterations());
EXPECT_EQ(10, timer_task2.iterations());
// Make sure resetting a timer before it has fired works. This will reset
// two timers, then stop the message loop between when they should have
// finally fired.
TimerTask timer_task3(100, false);
TimerTask timer_task4(600, false);
ResettingTask resetting_task3(50, false, &timer_task3);
ResettingTask resetting_task4(50, false, &timer_task4);
QuittingTask quitting_task(300, false);
TimerTask::RunTimers();
EXPECT_EQ(1, timer_task3.iterations());
EXPECT_EQ(0, timer_task4.iterations());
}
void RunTest_FifoOrder(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
// Creating timers with the same timeout should
// always compare to result in FIFO ordering.
// Derive from the timer so that we can set it's fire time.
// We have to do this, because otherwise, it's possible for
// two timers, created back to back, to have different times,
// and in that case, we aren't really testing what we want
// to test!
class MockTimer : public Timer {
public:
MockTimer(int delay) : Timer(delay, NULL, false) {}
void set_fire_time(const Time& t) { fire_time_ = t; }
};
class MockTimerManager : public TimerManager {
public:
MockTimerManager() : TimerManager(MessageLoop::current()) {
}
// Pops the most-recent to fire timer and returns its timer id.
// Returns -1 if there are no timers in the list.
int pop() {
int rv = -1;
Timer* top = PeekTopTimer();
if (top) {
rv = top->id();
StopTimer(top);
delete top;
}
return rv;
}
};
MockTimer t1(0);
MockTimer t2(0);
t2.set_fire_time(t1.fire_time());
TimerComparison comparison;
EXPECT_TRUE(comparison(&t2, &t1));
// Issue a tight loop of timers; most will have the
// same timestamp; some will not. Either way, since
// all are created with delay(0), the second timer
// must always be greater than the first. Then, pop
// all the timers and verify that it's a FIFO list.
MockTimerManager manager;
const int kNumTimers = 1024;
for (int i=0; i < kNumTimers; i++)
manager.StartTimer(0, NULL, false);
int last_id = -1;
int new_id = 0;
while((new_id = manager.pop()) > 0)
EXPECT_GT(new_id, last_id);
}
namespace {
class OneShotTimerTester {
public:
OneShotTimerTester(bool* did_run) : did_run_(did_run) {
}
void Start() {
timer_.Start(TimeDelta::FromMilliseconds(10), this,
&OneShotTimerTester::Run);
}
private:
void Run() {
*did_run_ = true;
MessageLoop::current()->Quit();
}
bool* did_run_;
base::OneShotTimer<OneShotTimerTester> timer_;
};
class RepeatingTimerTester {
public:
RepeatingTimerTester(bool* did_run) : did_run_(did_run), counter_(10) {
}
void Start() {
timer_.Start(TimeDelta::FromMilliseconds(10), this,
&RepeatingTimerTester::Run);
}
private:
void Run() {
if (--counter_ == 0) {
*did_run_ = true;
MessageLoop::current()->Quit();
}
}
bool* did_run_;
int counter_;
base::RepeatingTimer<RepeatingTimerTester> timer_;
};
} // namespace
void RunTest_OneShotTimer(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
bool did_run = false;
OneShotTimerTester f(&did_run);
f.Start();
MessageLoop::current()->Run();
EXPECT_TRUE(did_run);
}
void RunTest_OneShotTimer_Cancel(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
bool did_run_a = false;
OneShotTimerTester* a = new OneShotTimerTester(&did_run_a);
// This should run before the timer expires.
MessageLoop::current()->DeleteSoon(FROM_HERE, a);
// Now start the timer.
a->Start();
bool did_run_b = false;
OneShotTimerTester b(&did_run_b);
b.Start();
MessageLoop::current()->Run();
EXPECT_FALSE(did_run_a);
EXPECT_TRUE(did_run_b);
}
void RunTest_RepeatingTimer(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
bool did_run = false;
RepeatingTimerTester f(&did_run);
f.Start();
MessageLoop::current()->Run();
EXPECT_TRUE(did_run);
}
void RunTest_RepeatingTimer_Cancel(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
bool did_run_a = false;
RepeatingTimerTester* a = new RepeatingTimerTester(&did_run_a);
// This should run before the timer expires.
MessageLoop::current()->DeleteSoon(FROM_HERE, a);
// Now start the timer.
a->Start();
bool did_run_b = false;
RepeatingTimerTester b(&did_run_b);
b.Start();
MessageLoop::current()->Run();
EXPECT_FALSE(did_run_a);
EXPECT_TRUE(did_run_b);
}
} // namespace
//-----------------------------------------------------------------------------
// Each test is run against each type of MessageLoop. That way we are sure
// that timers work properly in all configurations.
TEST(TimerTest, TimerComparison) {
Time s = Time::Now();
RunTest_TimerComparison(MessageLoop::TYPE_DEFAULT);
RunTest_TimerComparison(MessageLoop::TYPE_UI);
RunTest_TimerComparison(MessageLoop::TYPE_IO);
Time e = Time::Now();
TimeDelta el = e - s;
printf("comparison elapsed time %lld\n", el.ToInternalValue());
}
TEST(TimerTest, BasicTimer) {
Time s = Time::Now();
RunTest_BasicTimer(MessageLoop::TYPE_DEFAULT);
RunTest_BasicTimer(MessageLoop::TYPE_UI);
RunTest_BasicTimer(MessageLoop::TYPE_IO);
Time e = Time::Now();
TimeDelta el = e - s;
printf("basic elapsed time %lld\n", el.ToInternalValue());
}
TEST(TimerTest, BrokenTimer) {
Time s = Time::Now();
RunTest_BrokenTimer(MessageLoop::TYPE_DEFAULT);
RunTest_BrokenTimer(MessageLoop::TYPE_UI);
RunTest_BrokenTimer(MessageLoop::TYPE_IO);
Time e = Time::Now();
TimeDelta el = e - s;
printf("broken elapsed time %lld\n", el.ToInternalValue());
}
TEST(TimerTest, DeleteFromRun) {
Time s = Time::Now();
RunTest_DeleteFromRun(MessageLoop::TYPE_DEFAULT);
RunTest_DeleteFromRun(MessageLoop::TYPE_UI);
RunTest_DeleteFromRun(MessageLoop::TYPE_IO);
Time e = Time::Now();
TimeDelta el = e - s;
printf("delete elapsed time %lld\n", el.ToInternalValue());
}
TEST(TimerTest, Reset) {
Time s = Time::Now();
RunTest_Reset(MessageLoop::TYPE_DEFAULT);
RunTest_Reset(MessageLoop::TYPE_UI);
RunTest_Reset(MessageLoop::TYPE_IO);
Time e = Time::Now();
TimeDelta el = e - s;
printf("reset elapsed time %lld\n", el.ToInternalValue());
}
TEST(TimerTest, FifoOrder) {
Time s = Time::Now();
RunTest_FifoOrder(MessageLoop::TYPE_DEFAULT);
RunTest_FifoOrder(MessageLoop::TYPE_UI);
RunTest_FifoOrder(MessageLoop::TYPE_IO);
Time e = Time::Now();
TimeDelta el = e - s;
printf("fifo elapsed time %lld\n", el.ToInternalValue());
}
TEST(TimerTest, OneShotTimer) {
RunTest_OneShotTimer(MessageLoop::TYPE_DEFAULT);
RunTest_OneShotTimer(MessageLoop::TYPE_UI);
RunTest_OneShotTimer(MessageLoop::TYPE_IO);
}
TEST(TimerTest, OneShotTimer_Cancel) {
RunTest_OneShotTimer_Cancel(MessageLoop::TYPE_DEFAULT);
RunTest_OneShotTimer_Cancel(MessageLoop::TYPE_UI);
RunTest_OneShotTimer_Cancel(MessageLoop::TYPE_IO);
}
TEST(TimerTest, RepeatingTimer) {
RunTest_RepeatingTimer(MessageLoop::TYPE_DEFAULT);
RunTest_RepeatingTimer(MessageLoop::TYPE_UI);
RunTest_RepeatingTimer(MessageLoop::TYPE_IO);
}
TEST(TimerTest, RepeatingTimer_Cancel) {
RunTest_RepeatingTimer_Cancel(MessageLoop::TYPE_DEFAULT);
RunTest_RepeatingTimer_Cancel(MessageLoop::TYPE_UI);
RunTest_RepeatingTimer_Cancel(MessageLoop::TYPE_IO);
}