// Copyright 2016 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/run_loop.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/location.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { namespace { void QuitWhenIdleTask(RunLoop* run_loop, int* counter) { run_loop->QuitWhenIdle(); ++(*counter); } void ShouldRunTask(int* counter) { ++(*counter); } void ShouldNotRunTask() { ADD_FAILURE() << "Ran a task that shouldn't run."; } void RunNestedLoopTask(int* counter) { RunLoop nested_run_loop; // This task should quit |nested_run_loop| but not the main RunLoop. ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, Bind(&QuitWhenIdleTask, Unretained(&nested_run_loop), Unretained(counter))); ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, Bind(&ShouldNotRunTask), TimeDelta::FromDays(1)); MessageLoop::ScopedNestableTaskAllower allower(MessageLoop::current()); nested_run_loop.Run(); ++(*counter); } class RunLoopTest : public testing::Test { protected: RunLoopTest() = default; MessageLoop message_loop_; RunLoop run_loop_; int counter_ = 0; private: DISALLOW_COPY_AND_ASSIGN(RunLoopTest); }; } // namespace TEST_F(RunLoopTest, QuitWhenIdle) { message_loop_.task_runner()->PostTask( FROM_HERE, Bind(&QuitWhenIdleTask, Unretained(&run_loop_), Unretained(&counter_))); message_loop_.task_runner()->PostTask( FROM_HERE, Bind(&ShouldRunTask, Unretained(&counter_))); message_loop_.task_runner()->PostDelayedTask( FROM_HERE, Bind(&ShouldNotRunTask), TimeDelta::FromDays(1)); run_loop_.Run(); EXPECT_EQ(2, counter_); } TEST_F(RunLoopTest, QuitWhenIdleNestedLoop) { message_loop_.task_runner()->PostTask( FROM_HERE, Bind(&RunNestedLoopTask, Unretained(&counter_))); message_loop_.task_runner()->PostTask( FROM_HERE, Bind(&QuitWhenIdleTask, Unretained(&run_loop_), Unretained(&counter_))); message_loop_.task_runner()->PostTask( FROM_HERE, Bind(&ShouldRunTask, Unretained(&counter_))); message_loop_.task_runner()->PostDelayedTask( FROM_HERE, Bind(&ShouldNotRunTask), TimeDelta::FromDays(1)); run_loop_.Run(); EXPECT_EQ(4, counter_); } TEST_F(RunLoopTest, QuitWhenIdleClosure) { message_loop_.task_runner()->PostTask(FROM_HERE, run_loop_.QuitWhenIdleClosure()); message_loop_.task_runner()->PostTask( FROM_HERE, Bind(&ShouldRunTask, Unretained(&counter_))); message_loop_.task_runner()->PostDelayedTask( FROM_HERE, Bind(&ShouldNotRunTask), TimeDelta::FromDays(1)); run_loop_.Run(); EXPECT_EQ(1, counter_); } // Verify that the QuitWhenIdleClosure() can run after the RunLoop has been // deleted. It should have no effect. TEST_F(RunLoopTest, QuitWhenIdleClosureAfterRunLoopScope) { Closure quit_when_idle_closure; { RunLoop run_loop; quit_when_idle_closure = run_loop.QuitWhenIdleClosure(); run_loop.RunUntilIdle(); } quit_when_idle_closure.Run(); } } // namespace base