// Copyright 2015 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 "chrome/browser/after_startup_task_utils.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/task_runner_util.h" #include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" #include "content/public/browser/browser_thread.h" #include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" using base::RunLoop; using content::BrowserThread; using content::TestBrowserThreadBundle; namespace { class WrappedTaskRunner : public base::TaskRunner { public: explicit WrappedTaskRunner(const scoped_refptr& real_runner) : real_task_runner_(real_runner) {} bool PostDelayedTask(const tracked_objects::Location& from_here, const base::Closure& task, base::TimeDelta delay) override { ++posted_task_count_; return real_task_runner_->PostDelayedTask( from_here, base::Bind(&WrappedTaskRunner::RunWrappedTask, this, task), base::TimeDelta()); // Squash all delays so our tests complete asap. } bool RunsTasksOnCurrentThread() const override { return real_task_runner_->RunsTasksOnCurrentThread(); } base::TaskRunner* real_runner() const { return real_task_runner_.get(); } int total_task_count() const { return posted_task_count_ + ran_task_count_; } int posted_task_count() const { return posted_task_count_; } int ran_task_count() const { return ran_task_count_; } void reset_task_counts() { posted_task_count_ = 0; ran_task_count_ = 0; } private: ~WrappedTaskRunner() override {} void RunWrappedTask(const base::Closure& task) { ++ran_task_count_; task.Run(); } scoped_refptr real_task_runner_; int posted_task_count_ = 0; int ran_task_count_ = 0; }; } // namespace class AfterStartupTaskTest : public testing::Test { public: AfterStartupTaskTest() : browser_thread_bundle_(TestBrowserThreadBundle::REAL_DB_THREAD) { ui_thread_ = new WrappedTaskRunner( BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)); db_thread_ = new WrappedTaskRunner( BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)); AfterStartupTaskUtils::UnsafeResetForTesting(); } // Hop to the db thread and call IsBrowserStartupComplete. bool GetIsBrowserStartupCompleteFromDBThread() { RunLoop run_loop; bool is_complete; base::PostTaskAndReplyWithResult( db_thread_->real_runner(), FROM_HERE, base::Bind(&AfterStartupTaskUtils::IsBrowserStartupComplete), base::Bind(&AfterStartupTaskTest::GotIsOnBrowserStartupComplete, &run_loop, &is_complete)); run_loop.Run(); return is_complete; } // Hop to the db thread and call PostAfterStartupTask. void PostAfterStartupTaskFromDBThread( const tracked_objects::Location& from_here, const scoped_refptr& task_runner, const base::Closure& task) { RunLoop run_loop; db_thread_->real_runner()->PostTaskAndReply( FROM_HERE, base::Bind(&AfterStartupTaskUtils::PostTask, from_here, task_runner, task), base::Bind(&RunLoop::Quit, base::Unretained(&run_loop))); run_loop.Run(); } // Make sure all tasks posted to the DB thread get run. void FlushDBThread() { RunLoop run_loop; db_thread_->real_runner()->PostTaskAndReply( FROM_HERE, base::Bind(&base::DoNothing), base::Bind(&RunLoop::Quit, base::Unretained(&run_loop))); run_loop.Run(); } static void VerifyExpectedThread(BrowserThread::ID id) { EXPECT_TRUE(BrowserThread::CurrentlyOn(id)); } protected: scoped_refptr ui_thread_; scoped_refptr db_thread_; private: static void GotIsOnBrowserStartupComplete(RunLoop* loop, bool* out, bool is_complete) { *out = is_complete; loop->Quit(); } TestBrowserThreadBundle browser_thread_bundle_; }; TEST_F(AfterStartupTaskTest, IsStartupComplete) { // Check IsBrowserStartupComplete on a background thread first to // verify that it does not allocate the underlying flag on that thread. // That allocation thread correctness part of this test relies on // the DCHECK in CancellationFlag::Set(). EXPECT_FALSE(GetIsBrowserStartupCompleteFromDBThread()); EXPECT_FALSE(AfterStartupTaskUtils::IsBrowserStartupComplete()); AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting(); EXPECT_TRUE(AfterStartupTaskUtils::IsBrowserStartupComplete()); EXPECT_TRUE(GetIsBrowserStartupCompleteFromDBThread()); } TEST_F(AfterStartupTaskTest, PostTask) { // Nothing should be posted prior to startup completion. EXPECT_FALSE(AfterStartupTaskUtils::IsBrowserStartupComplete()); AfterStartupTaskUtils::PostTask( FROM_HERE, ui_thread_, base::Bind(&AfterStartupTaskTest::VerifyExpectedThread, BrowserThread::UI)); AfterStartupTaskUtils::PostTask( FROM_HERE, db_thread_, base::Bind(&AfterStartupTaskTest::VerifyExpectedThread, BrowserThread::DB)); PostAfterStartupTaskFromDBThread( FROM_HERE, ui_thread_, base::Bind(&AfterStartupTaskTest::VerifyExpectedThread, BrowserThread::UI)); PostAfterStartupTaskFromDBThread( FROM_HERE, db_thread_, base::Bind(&AfterStartupTaskTest::VerifyExpectedThread, BrowserThread::DB)); RunLoop().RunUntilIdle(); EXPECT_EQ(0, db_thread_->total_task_count() + ui_thread_->total_task_count()); // Queued tasks should be posted upon setting the flag. AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting(); EXPECT_EQ(2, db_thread_->posted_task_count()); EXPECT_EQ(2, ui_thread_->posted_task_count()); FlushDBThread(); RunLoop().RunUntilIdle(); EXPECT_EQ(2, db_thread_->ran_task_count()); EXPECT_EQ(2, ui_thread_->ran_task_count()); db_thread_->reset_task_counts(); ui_thread_->reset_task_counts(); EXPECT_EQ(0, db_thread_->total_task_count() + ui_thread_->total_task_count()); // Tasks posted after startup should get posted immediately. AfterStartupTaskUtils::PostTask(FROM_HERE, ui_thread_, base::Bind(&base::DoNothing)); AfterStartupTaskUtils::PostTask(FROM_HERE, db_thread_, base::Bind(&base::DoNothing)); EXPECT_EQ(1, db_thread_->posted_task_count()); EXPECT_EQ(1, ui_thread_->posted_task_count()); PostAfterStartupTaskFromDBThread(FROM_HERE, ui_thread_, base::Bind(&base::DoNothing)); PostAfterStartupTaskFromDBThread(FROM_HERE, db_thread_, base::Bind(&base::DoNothing)); EXPECT_EQ(2, db_thread_->posted_task_count()); EXPECT_EQ(2, ui_thread_->posted_task_count()); FlushDBThread(); RunLoop().RunUntilIdle(); EXPECT_EQ(2, db_thread_->ran_task_count()); EXPECT_EQ(2, ui_thread_->ran_task_count()); }