blob: 6f3163967cebb74ea275f419b6cdfe4e98bf367a [file] [log] [blame]
// 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 <memory>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/task_runner_util.h"
#include "base/task_scheduler/post_task.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class WrappedTaskRunner : public base::TaskRunner {
public:
explicit WrappedTaskRunner(scoped_refptr<TaskRunner> real_runner)
: real_task_runner_(std::move(real_runner)) {}
bool PostDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
++posted_task_count_;
return real_task_runner_->PostDelayedTask(
from_here,
base::BindOnce(&WrappedTaskRunner::RunWrappedTask, this,
std::move(task)),
base::TimeDelta()); // Squash all delays so our tests complete asap.
}
bool RunsTasksInCurrentSequence() const override {
return real_task_runner_->RunsTasksInCurrentSequence();
}
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(base::OnceClosure task) {
++ran_task_count_;
std::move(task).Run();
}
scoped_refptr<TaskRunner> real_task_runner_;
int posted_task_count_ = 0;
int ran_task_count_ = 0;
};
} // namespace
class AfterStartupTaskTest : public testing::Test {
public:
AfterStartupTaskTest() {
ui_thread_ = base::MakeRefCounted<WrappedTaskRunner>(
content::BrowserThread::GetTaskRunnerForThread(
content::BrowserThread::UI));
background_sequence_ = base::MakeRefCounted<WrappedTaskRunner>(
base::CreateSequencedTaskRunnerWithTraits(base::TaskTraits()));
AfterStartupTaskUtils::UnsafeResetForTesting();
}
// Hop to the background sequence and call IsBrowserStartupComplete.
bool GetIsBrowserStartupCompleteFromBackgroundSequence() {
base::RunLoop run_loop;
bool is_complete;
base::PostTaskAndReplyWithResult(
background_sequence_->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 background sequence and call PostAfterStartupTask.
void PostAfterStartupTaskFromBackgroundSequence(
const base::Location& from_here,
scoped_refptr<base::TaskRunner> task_runner,
base::OnceClosure task) {
base::RunLoop run_loop;
background_sequence_->real_runner()->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&AfterStartupTaskUtils::PostTask, from_here,
std::move(task_runner), std::move(task)),
base::BindOnce(&base::RunLoop::Quit, base::Unretained(&run_loop)));
run_loop.Run();
}
// Make sure all tasks posted to the background sequence get run.
void FlushBackgroundSequence() {
base::RunLoop run_loop;
background_sequence_->real_runner()->PostTaskAndReply(
FROM_HERE, base::BindOnce(&base::DoNothing),
base::BindOnce(&base::RunLoop::Quit, base::Unretained(&run_loop)));
run_loop.Run();
}
static void VerifyExpectedSequence(base::TaskRunner* task_runner) {
EXPECT_TRUE(task_runner->RunsTasksInCurrentSequence());
}
protected:
scoped_refptr<WrappedTaskRunner> ui_thread_;
scoped_refptr<WrappedTaskRunner> background_sequence_;
private:
static void GotIsOnBrowserStartupComplete(base::RunLoop* loop,
bool* out,
bool is_complete) {
*out = is_complete;
loop->Quit();
}
content::TestBrowserThreadBundle browser_thread_bundle_;
};
TEST_F(AfterStartupTaskTest, IsStartupComplete) {
// Check IsBrowserStartupComplete on a background sequence first to
// verify that it does not allocate the underlying flag on that sequence.
// That allocation sequence correctness part of this test relies on
// the DCHECK in CancellationFlag::Set().
EXPECT_FALSE(GetIsBrowserStartupCompleteFromBackgroundSequence());
EXPECT_FALSE(AfterStartupTaskUtils::IsBrowserStartupComplete());
AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting();
EXPECT_TRUE(AfterStartupTaskUtils::IsBrowserStartupComplete());
EXPECT_TRUE(GetIsBrowserStartupCompleteFromBackgroundSequence());
}
TEST_F(AfterStartupTaskTest, PostTask) {
// Nothing should be posted prior to startup completion.
EXPECT_FALSE(AfterStartupTaskUtils::IsBrowserStartupComplete());
AfterStartupTaskUtils::PostTask(
FROM_HERE, ui_thread_,
base::BindOnce(&AfterStartupTaskTest::VerifyExpectedSequence,
base::RetainedRef(ui_thread_)));
AfterStartupTaskUtils::PostTask(
FROM_HERE, background_sequence_,
base::BindOnce(&AfterStartupTaskTest::VerifyExpectedSequence,
base::RetainedRef(background_sequence_)));
PostAfterStartupTaskFromBackgroundSequence(
FROM_HERE, ui_thread_,
base::BindOnce(&AfterStartupTaskTest::VerifyExpectedSequence,
base::RetainedRef(ui_thread_)));
PostAfterStartupTaskFromBackgroundSequence(
FROM_HERE, background_sequence_,
base::BindOnce(&AfterStartupTaskTest::VerifyExpectedSequence,
base::RetainedRef(background_sequence_)));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, background_sequence_->total_task_count() +
ui_thread_->total_task_count());
// Queued tasks should be posted upon setting the flag.
AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting();
EXPECT_EQ(2, background_sequence_->posted_task_count());
EXPECT_EQ(2, ui_thread_->posted_task_count());
FlushBackgroundSequence();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, background_sequence_->ran_task_count());
EXPECT_EQ(2, ui_thread_->ran_task_count());
background_sequence_->reset_task_counts();
ui_thread_->reset_task_counts();
EXPECT_EQ(0, background_sequence_->total_task_count() +
ui_thread_->total_task_count());
// Tasks posted after startup should get posted immediately.
AfterStartupTaskUtils::PostTask(FROM_HERE, ui_thread_,
base::BindOnce(&base::DoNothing));
AfterStartupTaskUtils::PostTask(FROM_HERE, background_sequence_,
base::BindOnce(&base::DoNothing));
EXPECT_EQ(1, background_sequence_->posted_task_count());
EXPECT_EQ(1, ui_thread_->posted_task_count());
PostAfterStartupTaskFromBackgroundSequence(FROM_HERE, ui_thread_,
base::BindOnce(&base::DoNothing));
PostAfterStartupTaskFromBackgroundSequence(FROM_HERE, background_sequence_,
base::BindOnce(&base::DoNothing));
EXPECT_EQ(2, background_sequence_->posted_task_count());
EXPECT_EQ(2, ui_thread_->posted_task_count());
FlushBackgroundSequence();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, background_sequence_->ran_task_count());
EXPECT_EQ(2, ui_thread_->ran_task_count());
}
// Verify that posting to an AfterStartupTaskUtils::Runner bound to
// |background_sequence_| results in the same behavior as posting via
// AfterStartupTaskUtils::PostTask(..., background_sequence_, ...).
TEST_F(AfterStartupTaskTest, AfterStartupTaskUtilsRunner) {
scoped_refptr<base::TaskRunner> after_startup_runner =
base::MakeRefCounted<AfterStartupTaskUtils::Runner>(background_sequence_);
EXPECT_FALSE(AfterStartupTaskUtils::IsBrowserStartupComplete());
after_startup_runner->PostTask(
FROM_HERE, base::BindOnce(&AfterStartupTaskTest::VerifyExpectedSequence,
base::RetainedRef(background_sequence_)));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(AfterStartupTaskUtils::IsBrowserStartupComplete());
EXPECT_EQ(0, background_sequence_->total_task_count());
AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting();
EXPECT_EQ(1, background_sequence_->posted_task_count());
FlushBackgroundSequence();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, background_sequence_->ran_task_count());
EXPECT_EQ(0, ui_thread_->total_task_count());
}