blob: 166144ea329d279c5174845a304c744ba3822ce4 [file] [log] [blame]
// Copyright 2014 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 <sys/timerfd.h>
#include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/timers/alarm_timer_chromeos.h"
#include "testing/gtest/include/gtest/gtest.h"
// Most of these tests have been lifted right out of timer_unittest.cc with only
// cosmetic changes. We want the AlarmTimer to be a drop-in replacement for the
// regular Timer so it should pass the same tests as the Timer class.
namespace timers {
namespace {
constexpr base::TimeDelta kTenMilliseconds =
base::TimeDelta::FromMilliseconds(10);
class AlarmTimerTester {
public:
AlarmTimerTester(bool* did_run,
base::TimeDelta delay,
base::OnceClosure quit_closure)
: did_run_(did_run),
quit_closure_(std::move(quit_closure)),
delay_(delay),
timer_(SimpleAlarmTimer::CreateForTesting()) {}
void Start() {
timer_->Start(
FROM_HERE, delay_,
base::BindRepeating(&AlarmTimerTester::Run, base::Unretained(this)));
}
private:
void Run() {
*did_run_ = true;
if (quit_closure_)
std::move(quit_closure_).Run();
}
bool* did_run_;
base::OnceClosure quit_closure_;
const base::TimeDelta delay_;
std::unique_ptr<SimpleAlarmTimer> timer_;
DISALLOW_COPY_AND_ASSIGN(AlarmTimerTester);
};
class SelfDeletingAlarmTimerTester {
public:
SelfDeletingAlarmTimerTester(bool* did_run,
base::TimeDelta delay,
base::OnceClosure quit_closure)
: did_run_(did_run),
quit_closure_(std::move(quit_closure)),
delay_(delay),
timer_(SimpleAlarmTimer::CreateForTesting()) {}
void Start() {
timer_->Start(FROM_HERE, delay_,
base::BindRepeating(&SelfDeletingAlarmTimerTester::Run,
base::Unretained(this)));
}
private:
void Run() {
*did_run_ = true;
timer_.reset();
if (quit_closure_)
std::move(quit_closure_).Run();
}
bool* did_run_;
base::OnceClosure quit_closure_;
const base::TimeDelta delay_;
std::unique_ptr<SimpleAlarmTimer> timer_;
DISALLOW_COPY_AND_ASSIGN(SelfDeletingAlarmTimerTester);
};
} // namespace
//-----------------------------------------------------------------------------
// Each test is run against each type of MessageLoop. That way we are sure
// that timers work properly in all configurations.
TEST(AlarmTimerTest, SimpleAlarmTimer) {
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
base::RunLoop run_loop;
bool did_run = false;
AlarmTimerTester f(&did_run, kTenMilliseconds,
run_loop.QuitWhenIdleClosure());
f.Start();
run_loop.Run();
EXPECT_TRUE(did_run);
}
TEST(AlarmTimerTest, SimpleAlarmTimer_Cancel) {
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
bool did_run_a = false;
AlarmTimerTester* a =
new AlarmTimerTester(&did_run_a, kTenMilliseconds, base::OnceClosure());
// This should run before the timer expires.
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
// Now start the timer.
a->Start();
base::RunLoop run_loop;
bool did_run_b = false;
AlarmTimerTester b(&did_run_b, kTenMilliseconds,
run_loop.QuitWhenIdleClosure());
b.Start();
run_loop.Run();
EXPECT_FALSE(did_run_a);
EXPECT_TRUE(did_run_b);
}
// If underlying timer does not handle this properly, we will crash or fail
// in full page heap environment.
TEST(AlarmTimerTest, SelfDeletingAlarmTimer) {
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
base::RunLoop run_loop;
bool did_run = false;
SelfDeletingAlarmTimerTester f(&did_run, kTenMilliseconds,
run_loop.QuitWhenIdleClosure());
f.Start();
run_loop.Run();
EXPECT_TRUE(did_run);
}
TEST(AlarmTimerTest, AlarmTimerZeroDelay) {
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
base::RunLoop run_loop;
bool did_run = false;
AlarmTimerTester f(&did_run, base::TimeDelta(),
run_loop.QuitWhenIdleClosure());
f.Start();
run_loop.Run();
EXPECT_TRUE(did_run);
}
TEST(AlarmTimerTest, AlarmTimerZeroDelay_Cancel) {
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
bool did_run_a = false;
AlarmTimerTester* a =
new AlarmTimerTester(&did_run_a, base::TimeDelta(), base::OnceClosure());
// This should run before the timer expires.
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
// Now start the timer.
a->Start();
base::RunLoop run_loop;
bool did_run_b = false;
AlarmTimerTester b(&did_run_b, base::TimeDelta(),
run_loop.QuitWhenIdleClosure());
b.Start();
run_loop.Run();
EXPECT_FALSE(did_run_a);
EXPECT_TRUE(did_run_b);
}
TEST(AlarmTimerTest, 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 is enabled.
bool did_run = false;
{
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
AlarmTimerTester a(&did_run, kTenMilliseconds, base::OnceClosure());
AlarmTimerTester b(&did_run, kTenMilliseconds, base::OnceClosure());
AlarmTimerTester c(&did_run, kTenMilliseconds, base::OnceClosure());
AlarmTimerTester d(&did_run, kTenMilliseconds, base::OnceClosure());
a.Start();
b.Start();
// Allow FileDescriptorWatcher to start watching the timers. Without this,
// tasks posted by FileDescriptorWatcher::WatchReadable() are leaked.
base::RunLoop().RunUntilIdle();
} // SimpleAlarmTimers destruct. SHOULD NOT CRASH, of course.
EXPECT_FALSE(did_run);
}
TEST(AlarmTimerTest, NonRepeatIsRunning) {
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
auto timer = SimpleAlarmTimer::CreateForTesting();
EXPECT_FALSE(timer->IsRunning());
timer->Start(FROM_HERE, base::TimeDelta::FromDays(1), base::DoNothing());
// Allow FileDescriptorWatcher to start watching the timer. Without this, a
// task posted by FileDescriptorWatcher::WatchReadable() is leaked.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(timer->IsRunning());
timer->Stop();
EXPECT_FALSE(timer->IsRunning());
ASSERT_FALSE(timer->user_task().is_null());
timer->Reset();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(timer->IsRunning());
}
TEST(AlarmTimerTest, RetainNonRepeatIsRunning) {
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
auto timer = SimpleAlarmTimer::CreateForTesting();
EXPECT_FALSE(timer->IsRunning());
timer->Start(FROM_HERE, base::TimeDelta::FromDays(1), base::DoNothing());
// Allow FileDescriptorWatcher to start watching the timer. Without this, a
// task posted by FileDescriptorWatcher::WatchReadable() is leaked.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(timer->IsRunning());
timer->Reset();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(timer->IsRunning());
timer->Stop();
EXPECT_FALSE(timer->IsRunning());
timer->Reset();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(timer->IsRunning());
}
namespace {
bool g_callback_happened1 = false;
bool g_callback_happened2 = false;
void ClearAllCallbackHappened() {
g_callback_happened1 = false;
g_callback_happened2 = false;
}
void SetCallbackHappened1(base::OnceClosure quit_closure) {
g_callback_happened1 = true;
if (quit_closure)
std::move(quit_closure).Run();
}
void SetCallbackHappened2(base::OnceClosure quit_closure) {
g_callback_happened2 = true;
if (quit_closure)
std::move(quit_closure).Run();
}
TEST(AlarmTimerTest, ContinuationStopStart) {
ClearAllCallbackHappened();
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
auto timer = SimpleAlarmTimer::CreateForTesting();
timer->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
base::BindRepeating(&SetCallbackHappened1,
base::DoNothing().Repeatedly()));
timer->Stop();
base::RunLoop run_loop;
timer->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(40),
base::BindRepeating(&SetCallbackHappened2,
run_loop.QuitWhenIdleClosure()));
run_loop.Run();
EXPECT_FALSE(g_callback_happened1);
EXPECT_TRUE(g_callback_happened2);
}
TEST(AlarmTimerTest, ContinuationReset) {
ClearAllCallbackHappened();
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
base::RunLoop run_loop;
auto timer = SimpleAlarmTimer::CreateForTesting();
timer->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
base::BindRepeating(&SetCallbackHappened1,
run_loop.QuitWhenIdleClosure()));
timer->Reset();
ASSERT_FALSE(timer->user_task().is_null());
run_loop.Run();
EXPECT_TRUE(g_callback_happened1);
}
// Verify that no crash occurs if a timer is deleted while its callback is
// running.
TEST(AlarmTimerTest, DeleteTimerWhileCallbackIsRunning) {
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
base::RunLoop run_loop;
// Will be deleted by the callback.
auto timer = SimpleAlarmTimer::CreateForTesting();
auto* timer_ptr = timer.get();
timer_ptr->Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(10),
base::BindRepeating([](std::unique_ptr<SimpleAlarmTimer> timer,
base::RunLoop* run_loop) { run_loop->Quit(); },
base::Passed(std::move(timer)), &run_loop));
run_loop.Run();
}
// Verify that no crash occurs if a zero-delay timer is deleted while its
// callback is running.
TEST(AlarmTimerTest, DeleteTimerWhileCallbackIsRunningZeroDelay) {
base::test::ScopedTaskEnvironment task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
base::RunLoop run_loop;
// Will be deleted by the callback.
auto timer = SimpleAlarmTimer::CreateForTesting();
auto* timer_ptr = timer.get();
timer_ptr->Start(
FROM_HERE, base::TimeDelta(),
base::BindRepeating([](std::unique_ptr<SimpleAlarmTimer> timer,
base::RunLoop* run_loop) { run_loop->Quit(); },
base::Passed(std::move(timer)), &run_loop));
run_loop.Run();
}
} // namespace
} // namespace timers