// Copyright (c) 2011 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/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "base/timer.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::TimeDelta;
namespace {
class OneShotTimerTester {
public:
OneShotTimerTester(bool* did_run, unsigned milliseconds = 10)
: did_run_(did_run),
delay_ms_(milliseconds) {
}
void Start() {
timer_.Start(TimeDelta::FromMilliseconds(delay_ms_), this,
&OneShotTimerTester::Run);
}
private:
void Run() {
*did_run_ = true;
MessageLoop::current()->Quit();
}
bool* did_run_;
base::OneShotTimer<OneShotTimerTester> timer_;
const unsigned delay_ms_;
};
class OneShotSelfDeletingTimerTester {
public:
explicit OneShotSelfDeletingTimerTester(bool* did_run) :
did_run_(did_run),
timer_(new base::OneShotTimer<OneShotSelfDeletingTimerTester>()) {
}
void Start() {
timer_->Start(TimeDelta::FromMilliseconds(10), this,
&OneShotSelfDeletingTimerTester::Run);
}
private:
void Run() {
*did_run_ = true;
timer_.reset();
MessageLoop::current()->Quit();
}
bool* did_run_;
scoped_ptr<base::OneShotTimer<OneShotSelfDeletingTimerTester> > timer_;
};
class RepeatingTimerTester {
public:
explicit 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_;
};
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_OneShotSelfDeletingTimer(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
bool did_run = false;
OneShotSelfDeletingTimerTester f(&did_run);
f.Start();
MessageLoop::current()->Run();
EXPECT_TRUE(did_run);
}
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);
}
class DelayTimerTarget {
public:
DelayTimerTarget()
: signaled_(false) {
}
bool signaled() const { return signaled_; }
void Signal() {
ASSERT_FALSE(signaled_);
signaled_ = true;
}
private:
bool signaled_;
};
void RunTest_DelayTimer_NoCall(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
// If Delay is never called, the timer shouldn't go off.
DelayTimerTarget target;
base::DelayTimer<DelayTimerTarget> timer(
TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal);
bool did_run = false;
OneShotTimerTester tester(&did_run);
tester.Start();
MessageLoop::current()->Run();
ASSERT_FALSE(target.signaled());
}
void RunTest_DelayTimer_OneCall(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
DelayTimerTarget target;
base::DelayTimer<DelayTimerTarget> timer(
TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal);
timer.Reset();
bool did_run = false;
OneShotTimerTester tester(&did_run, 100 /* milliseconds */);
tester.Start();
MessageLoop::current()->Run();
ASSERT_TRUE(target.signaled());
}
struct ResetHelper {
ResetHelper(base::DelayTimer<DelayTimerTarget>* timer,
DelayTimerTarget* target)
: timer_(timer),
target_(target) {
}
void Reset() {
ASSERT_FALSE(target_->signaled());
timer_->Reset();
}
private:
base::DelayTimer<DelayTimerTarget> *const timer_;
DelayTimerTarget *const target_;
};
void RunTest_DelayTimer_Reset(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
// If Delay is never called, the timer shouldn't go off.
DelayTimerTarget target;
base::DelayTimer<DelayTimerTarget> timer(
TimeDelta::FromMilliseconds(50), &target, &DelayTimerTarget::Signal);
timer.Reset();
ResetHelper reset_helper(&timer, &target);
base::OneShotTimer<ResetHelper> timers[20];
for (size_t i = 0; i < arraysize(timers); ++i) {
timers[i].Start(TimeDelta::FromMilliseconds(i * 10), &reset_helper,
&ResetHelper::Reset);
}
bool did_run = false;
OneShotTimerTester tester(&did_run, 300);
tester.Start();
MessageLoop::current()->Run();
ASSERT_TRUE(target.signaled());
}
class DelayTimerFatalTarget {
public:
void Signal() {
ASSERT_TRUE(false);
}
};
void RunTest_DelayTimer_Deleted(MessageLoop::Type message_loop_type) {
MessageLoop loop(message_loop_type);
DelayTimerFatalTarget target;
{
base::DelayTimer<DelayTimerFatalTarget> timer(
TimeDelta::FromMilliseconds(50), &target,
&DelayTimerFatalTarget::Signal);
timer.Reset();
}
// When the timer is deleted, the DelayTimerFatalTarget should never be
// called.
base::PlatformThread::Sleep(100);
}
} // namespace
//-----------------------------------------------------------------------------
// Each test is run against each type of MessageLoop. That way we are sure
// that timers work properly in all configurations.
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);
}
// If underline timer does not handle properly, we will crash or fail
// in full page heap or purify environment.
TEST(TimerTest, OneShotSelfDeletingTimer) {
RunTest_OneShotSelfDeletingTimer(MessageLoop::TYPE_DEFAULT);
RunTest_OneShotSelfDeletingTimer(MessageLoop::TYPE_UI);
RunTest_OneShotSelfDeletingTimer(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);
}
TEST(TimerTest, DelayTimer_NoCall) {
RunTest_DelayTimer_NoCall(MessageLoop::TYPE_DEFAULT);
RunTest_DelayTimer_NoCall(MessageLoop::TYPE_UI);
RunTest_DelayTimer_NoCall(MessageLoop::TYPE_IO);
}
TEST(TimerTest, DelayTimer_OneCall) {
RunTest_DelayTimer_OneCall(MessageLoop::TYPE_DEFAULT);
RunTest_DelayTimer_OneCall(MessageLoop::TYPE_UI);
RunTest_DelayTimer_OneCall(MessageLoop::TYPE_IO);
}
// It's flaky on the buildbot, http://crbug.com/25038.
TEST(TimerTest, FLAKY_DelayTimer_Reset) {
RunTest_DelayTimer_Reset(MessageLoop::TYPE_DEFAULT);
RunTest_DelayTimer_Reset(MessageLoop::TYPE_UI);
RunTest_DelayTimer_Reset(MessageLoop::TYPE_IO);
}
TEST(TimerTest, DelayTimer_Deleted) {
RunTest_DelayTimer_Deleted(MessageLoop::TYPE_DEFAULT);
RunTest_DelayTimer_Deleted(MessageLoop::TYPE_UI);
RunTest_DelayTimer_Deleted(MessageLoop::TYPE_IO);
}
TEST(TimerTest, MessageLoopShutdown) {
// This test is designed to verify that shutdown of the
// message loop does not cause crashes if there were pending
// timers not yet fired. It may only trigger exceptions
// if debug heap checking (or purify) is enabled.
bool did_run = false;
{
OneShotTimerTester a(&did_run);
OneShotTimerTester b(&did_run);
OneShotTimerTester c(&did_run);
OneShotTimerTester d(&did_run);
{
MessageLoop loop(MessageLoop::TYPE_DEFAULT);
a.Start();
b.Start();
} // MessageLoop destructs by falling out of scope.
} // OneShotTimers destruct. SHOULD NOT CRASH, of course.
EXPECT_FALSE(did_run);
}