summaryrefslogtreecommitdiff
path: root/components/timers/alarm_timer_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'components/timers/alarm_timer_unittest.cc')
-rw-r--r--components/timers/alarm_timer_unittest.cc372
1 files changed, 372 insertions, 0 deletions
diff --git a/components/timers/alarm_timer_unittest.cc b/components/timers/alarm_timer_unittest.cc
new file mode 100644
index 0000000000..868eb787ab
--- /dev/null
+++ b/components/timers/alarm_timer_unittest.cc
@@ -0,0 +1,372 @@
+// 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/files/file_descriptor_watcher_posix.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.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 {
+const 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_(new timers::SimpleAlarmTimer()) {}
+ 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<timers::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_(new timers::SimpleAlarmTimer()) {}
+ 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<timers::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::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+ 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::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+ 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::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+ 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::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+ 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::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+ 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;
+ {
+ auto loop = std::make_unique<base::MessageLoopForIO>();
+ auto file_descriptor_watcher =
+ std::make_unique<base::FileDescriptorWatcher>(loop.get());
+ 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();
+
+ // MessageLoop and FileDescriptorWatcher destruct.
+ file_descriptor_watcher.reset();
+ loop.reset();
+ } // SimpleAlarmTimers destruct. SHOULD NOT CRASH, of course.
+
+ EXPECT_FALSE(did_run);
+}
+
+TEST(AlarmTimerTest, NonRepeatIsRunning) {
+ {
+ base::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+ timers::SimpleAlarmTimer timer;
+ 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::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+ timers::SimpleAlarmTimer timer;
+ 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::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+ timers::SimpleAlarmTimer timer;
+ 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::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+ base::RunLoop run_loop;
+ timers::SimpleAlarmTimer timer;
+ 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::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+ base::RunLoop run_loop;
+
+ // Will be deleted by the callback.
+ timers::SimpleAlarmTimer* timer = new timers::SimpleAlarmTimer;
+
+ timer->Start(
+ FROM_HERE, base::TimeDelta::FromMilliseconds(10),
+ base::BindRepeating(
+ [](timers::SimpleAlarmTimer* timer, base::RunLoop* run_loop) {
+ delete timer;
+ run_loop->Quit();
+ },
+ 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::MessageLoopForIO loop;
+ base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+ base::RunLoop run_loop;
+
+ // Will be deleted by the callback.
+ timers::SimpleAlarmTimer* timer = new timers::SimpleAlarmTimer;
+
+ timer->Start(
+ FROM_HERE, base::TimeDelta(),
+ base::BindRepeating(
+ [](timers::SimpleAlarmTimer* timer, base::RunLoop* run_loop) {
+ delete timer;
+ run_loop->Quit();
+ },
+ timer, &run_loop));
+ run_loop.Run();
+}
+
+} // namespace
+} // namespace timers