summaryrefslogtreecommitdiff
path: root/chromium/base/run_loop_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/base/run_loop_unittest.cc')
-rw-r--r--chromium/base/run_loop_unittest.cc221
1 files changed, 179 insertions, 42 deletions
diff --git a/chromium/base/run_loop_unittest.cc b/chromium/base/run_loop_unittest.cc
index beebda30e70..96060f4a660 100644
--- a/chromium/base/run_loop_unittest.cc
+++ b/chromium/base/run_loop_unittest.cc
@@ -87,7 +87,7 @@ class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner {
return origin_thread_checker_.CalledOnValidThread();
}
- bool ProcessTask() {
+ bool ProcessSingleTask() {
OnceClosure task;
{
AutoLock auto_lock(tasks_lock_);
@@ -97,11 +97,18 @@ class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner {
pending_tasks_.pop();
}
// It's important to Run() after pop() and outside the lock as |task| may
- // run a nested loop which will re-enter ProcessTask().
+ // run a nested loop which will re-enter ProcessSingleTask().
std::move(task).Run();
return true;
}
+ base::queue<OnceClosure> TakePendingTasks() {
+ AutoLock auto_lock(tasks_lock_);
+ base::queue<OnceClosure> pending_tasks;
+ std::swap(pending_tasks, pending_tasks_);
+ return pending_tasks;
+ }
+
private:
~SimpleSingleThreadTaskRunner() override = default;
@@ -116,49 +123,65 @@ class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner {
DISALLOW_COPY_AND_ASSIGN(SimpleSingleThreadTaskRunner);
};
+// The basis of all TestDelegates, allows safely injecting a OnceClosure to be
+// run in the next idle phase of this delegate's Run() implementation. This can
+// be used to have code run on a thread that is otherwise livelocked in an idle
+// phase (sometimes a simple PostTask() won't do it -- e.g. when processing
+// application tasks is disallowed).
+class InjectableTestDelegate : public RunLoop::Delegate {
+ public:
+ void InjectClosureOnDelegate(OnceClosure closure) {
+ AutoLock auto_lock(closure_lock_);
+ closure_ = std::move(closure);
+ }
+
+ bool RunInjectedClosure() {
+ AutoLock auto_lock(closure_lock_);
+ if (closure_.is_null())
+ return false;
+ std::move(closure_).Run();
+ return true;
+ }
+
+ private:
+ Lock closure_lock_;
+ OnceClosure closure_;
+};
+
// A simple test RunLoop::Delegate to exercise Runloop logic independent of any
-// other base constructs.
-class TestDelegate final : public RunLoop::Delegate {
+// other base constructs. BindToCurrentThread() must be called before this
+// TestBoundDelegate is operational.
+class TestBoundDelegate final : public InjectableTestDelegate {
public:
- TestDelegate() = default;
+ TestBoundDelegate() = default;
+ // Makes this TestBoundDelegate become the RunLoop::Delegate and
+ // ThreadTaskRunnerHandle for this thread.
void BindToCurrentThread() {
thread_task_runner_handle_ =
std::make_unique<ThreadTaskRunnerHandle>(simple_task_runner_);
- run_loop_client_ = RunLoop::RegisterDelegateForCurrentThread(this);
- }
-
- // Runs |closure| on the TestDelegate thread as part of Run(). Useful to
- // inject code in an otherwise livelocked Run() state.
- void RunClosureOnDelegate(OnceClosure closure) {
- AutoLock auto_lock(closure_lock_);
- closure_ = std::move(closure);
+ RunLoop::RegisterDelegateForCurrentThread(this);
}
private:
void Run(bool application_tasks_allowed) override {
if (nested_run_allowing_tasks_incoming_) {
- EXPECT_TRUE(run_loop_client_->IsNested());
+ EXPECT_TRUE(RunLoop::IsNestedOnCurrentThread());
EXPECT_TRUE(application_tasks_allowed);
- } else if (run_loop_client_->IsNested()) {
+ } else if (RunLoop::IsNestedOnCurrentThread()) {
EXPECT_FALSE(application_tasks_allowed);
}
nested_run_allowing_tasks_incoming_ = false;
while (!should_quit_) {
- if (application_tasks_allowed && simple_task_runner_->ProcessTask())
+ if (application_tasks_allowed && simple_task_runner_->ProcessSingleTask())
continue;
- if (run_loop_client_->ShouldQuitWhenIdle())
+ if (ShouldQuitWhenIdle())
break;
- {
- AutoLock auto_lock(closure_lock_);
- if (!closure_.is_null()) {
- std::move(closure_).Run();
- continue;
- }
- }
+ if (RunInjectedClosure())
+ continue;
PlatformThread::YieldCurrentThread();
}
@@ -177,14 +200,80 @@ class TestDelegate final : public RunLoop::Delegate {
scoped_refptr<SimpleSingleThreadTaskRunner> simple_task_runner_ =
MakeRefCounted<SimpleSingleThreadTaskRunner>();
+
std::unique_ptr<ThreadTaskRunnerHandle> thread_task_runner_handle_;
bool should_quit_ = false;
+};
- Lock closure_lock_;
- OnceClosure closure_;
+// A test RunLoop::Delegate meant to override an existing RunLoop::Delegate.
+// TakeOverCurrentThread() must be called before this TestBoundDelegate is
+// operational.
+class TestOverridingDelegate final : public InjectableTestDelegate {
+ public:
+ TestOverridingDelegate() = default;
+
+ // Overrides the existing RunLoop::Delegate and ThreadTaskRunnerHandles on
+ // this thread with this TestOverridingDelegate's.
+ void TakeOverCurrentThread() {
+ overridden_task_runner_ = ThreadTaskRunnerHandle::Get();
+ ASSERT_TRUE(overridden_task_runner_);
+ thread_task_runner_handle_override_scope_ =
+ ThreadTaskRunnerHandle::OverrideForTesting(
+ simple_task_runner_,
+ ThreadTaskRunnerHandle::OverrideType::kTakeOverThread);
+
+ // TestOverridingDelegate::Run() is designed with the assumption that the
+ // overridden Delegate's Run() always returns control to it when it becomes
+ // idle.
+ overridden_delegate_ = RunLoop::OverrideDelegateForCurrentThreadForTesting(
+ this, base::BindRepeating([]() { return true; }));
+ ASSERT_TRUE(overridden_delegate_);
+ }
+
+ private:
+ void Run(bool application_tasks_allowed) override {
+ while (!should_quit_) {
+ auto pending_tasks = simple_task_runner_->TakePendingTasks();
+ if (!pending_tasks.empty()) {
+ while (!pending_tasks.empty()) {
+ overridden_task_runner_->PostTask(FROM_HERE,
+ std::move(pending_tasks.front()));
+ pending_tasks.pop();
+ }
+ overridden_delegate_->Run(application_tasks_allowed);
+ continue;
+ }
+
+ if (ShouldQuitWhenIdle())
+ break;
+
+ if (RunInjectedClosure())
+ continue;
+
+ PlatformThread::YieldCurrentThread();
+ }
+ should_quit_ = false;
+ }
+
+ void Quit() override {
+ should_quit_ = true;
+ overridden_delegate_->Quit();
+ }
+
+ void EnsureWorkScheduled() override {
+ overridden_delegate_->EnsureWorkScheduled();
+ }
- RunLoop::Delegate::Client* run_loop_client_ = nullptr;
+ scoped_refptr<SimpleSingleThreadTaskRunner> simple_task_runner_ =
+ MakeRefCounted<SimpleSingleThreadTaskRunner>();
+
+ ScopedClosureRunner thread_task_runner_handle_override_scope_;
+
+ scoped_refptr<SingleThreadTaskRunner> overridden_task_runner_;
+ RunLoop::Delegate* overridden_delegate_;
+
+ bool should_quit_ = false;
};
enum class RunLoopTestType {
@@ -195,6 +284,10 @@ enum class RunLoopTestType {
// Runs all RunLoopTests under a test RunLoop::Delegate to make sure the
// delegate interface fully works standalone.
kTestDelegate,
+
+ // Runs all RunLoopTests through a RunLoop::Delegate which overrides a
+ // kRealEnvironment's registered RunLoop::Delegate.
+ kOverridingTestDelegate,
};
// The task environment for the RunLoopTest of a given type. A separate class
@@ -203,20 +296,31 @@ class RunLoopTestEnvironment {
public:
RunLoopTestEnvironment(RunLoopTestType type) {
switch (type) {
- case RunLoopTestType::kRealEnvironment:
+ case RunLoopTestType::kRealEnvironment: {
task_environment_ = std::make_unique<test::ScopedTaskEnvironment>();
break;
- case RunLoopTestType::kTestDelegate:
- test_delegate_ = std::make_unique<TestDelegate>();
- test_delegate_->BindToCurrentThread();
+ }
+ case RunLoopTestType::kTestDelegate: {
+ auto test_delegate = std::make_unique<TestBoundDelegate>();
+ test_delegate->BindToCurrentThread();
+ test_delegate_ = std::move(test_delegate);
break;
+ }
+ case RunLoopTestType::kOverridingTestDelegate: {
+ task_environment_ = std::make_unique<test::ScopedTaskEnvironment>();
+ auto test_delegate = std::make_unique<TestOverridingDelegate>();
+ test_delegate->TakeOverCurrentThread();
+ test_delegate_ = std::move(test_delegate);
+ break;
+ }
}
}
private:
- // Instantiates one or the other based on the RunLoopTestType.
+ // Instantiates one or the other based on the RunLoopTestType (or both in the
+ // kOverridingTestDelegate case).
std::unique_ptr<test::ScopedTaskEnvironment> task_environment_;
- std::unique_ptr<TestDelegate> test_delegate_;
+ std::unique_ptr<InjectableTestDelegate> test_delegate_;
};
class RunLoopTest : public testing::TestWithParam<RunLoopTestType> {
@@ -466,21 +570,37 @@ TEST_P(RunLoopTest, IsNestedOnCurrentThread) {
run_loop_.Run();
}
+namespace {
+
class MockNestingObserver : public RunLoop::NestingObserver {
public:
MockNestingObserver() = default;
// RunLoop::NestingObserver:
MOCK_METHOD0(OnBeginNestedRunLoop, void());
+ MOCK_METHOD0(OnExitNestedRunLoop, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockNestingObserver);
};
+class MockTask {
+ public:
+ MockTask() = default;
+ MOCK_METHOD0(Task, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockTask);
+};
+
+} // namespace
+
TEST_P(RunLoopTest, NestingObservers) {
EXPECT_TRUE(RunLoop::IsNestingAllowedOnCurrentThread());
testing::StrictMock<MockNestingObserver> nesting_observer;
+ testing::StrictMock<MockTask> mock_task_a;
+ testing::StrictMock<MockTask> mock_task_b;
RunLoop::AddNestingObserverOnCurrentThread(&nesting_observer);
@@ -495,14 +615,27 @@ TEST_P(RunLoopTest, NestingObservers) {
nested_run_loop.Run();
});
- // Generate a stack of nested RunLoops, an OnBeginNestedRunLoop() is
- // expected when beginning each nesting depth.
+ // Generate a stack of nested RunLoops. OnBeginNestedRunLoop() is expected
+ // when beginning each nesting depth and OnExitNestedRunLoop() is expected
+ // when exiting each nesting depth.
ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop);
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_a)));
ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop);
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop_.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_b)));
- EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop()).Times(2);
- run_loop_.Run();
+ {
+ testing::InSequence in_sequence;
+ EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop());
+ EXPECT_CALL(mock_task_a, Task());
+ EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop());
+ EXPECT_CALL(mock_task_b, Task());
+ EXPECT_CALL(nesting_observer, OnExitNestedRunLoop()).Times(2);
+ }
+ run_loop_.RunUntilIdle();
RunLoop::RemoveNestingObserverOnCurrentThread(&nesting_observer);
}
@@ -539,15 +672,19 @@ INSTANTIATE_TEST_CASE_P(Real,
INSTANTIATE_TEST_CASE_P(Mock,
RunLoopTest,
testing::Values(RunLoopTestType::kTestDelegate));
+INSTANTIATE_TEST_CASE_P(
+ OverridingMock,
+ RunLoopTest,
+ testing::Values(RunLoopTestType::kOverridingTestDelegate));
TEST(RunLoopDeathTest, MustRegisterBeforeInstantiating) {
- TestDelegate unbound_test_delegate_;
+ TestBoundDelegate unbound_test_delegate_;
// Exercise the DCHECK in RunLoop::RunLoop().
EXPECT_DCHECK_DEATH({ RunLoop(); });
}
TEST(RunLoopDelegateTest, NestableTasksDontRunInDefaultNestedLoops) {
- TestDelegate test_delegate;
+ TestBoundDelegate test_delegate;
test_delegate.BindToCurrentThread();
base::Thread other_thread("test");
@@ -599,8 +736,8 @@ TEST(RunLoopDelegateTest, NestableTasksDontRunInDefaultNestedLoops) {
other_thread.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(
- [](TestDelegate* test_delegate, OnceClosure injected_closure) {
- test_delegate->RunClosureOnDelegate(std::move(injected_closure));
+ [](TestBoundDelegate* test_delegate, OnceClosure injected_closure) {
+ test_delegate->InjectClosureOnDelegate(std::move(injected_closure));
},
Unretained(&test_delegate), nested_run_loop.QuitWhenIdleClosure()),
TestTimeouts::tiny_timeout());