1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
// Copyright 2015 The Chromium Authors
// 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/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class WrappedTaskRunner : public base::SequencedTaskRunner {
public:
explicit WrappedTaskRunner(scoped_refptr<SequencedTaskRunner> 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 PostNonNestableDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
// Not implemented.
NOTREACHED();
}
bool RunsTasksInCurrentSequence() const override {
return real_task_runner_->RunsTasksInCurrentSequence();
}
base::SequencedTaskRunner* 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 = default;
void RunWrappedTask(base::OnceClosure task) {
++ran_task_count_;
std::move(task).Run();
}
scoped_refptr<base::SequencedTaskRunner> 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::GetUIThreadTaskRunner({}));
background_sequence_ = base::MakeRefCounted<WrappedTaskRunner>(
base::ThreadPool::CreateSequencedTaskRunner({}));
AfterStartupTaskUtils::UnsafeResetForTesting();
}
// Hop to the background sequence and call IsBrowserStartupComplete.
bool GetIsBrowserStartupCompleteFromBackgroundSequence() {
base::RunLoop run_loop;
bool is_complete;
background_sequence_->real_runner()->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&AfterStartupTaskUtils::IsBrowserStartupComplete),
base::BindOnce(&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::SequencedTaskRunner> 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::BindLambdaForTesting([&]() { run_loop.Quit(); }));
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::DoNothing(),
base::BindLambdaForTesting([&]() { run_loop.Quit(); }));
run_loop.Run();
}
static void VerifyExpectedSequence(base::SequencedTaskRunner* 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::BrowserTaskEnvironment task_environment_;
};
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::DoNothing());
AfterStartupTaskUtils::PostTask(FROM_HERE, background_sequence_,
base::DoNothing());
EXPECT_EQ(1, background_sequence_->posted_task_count());
EXPECT_EQ(1, ui_thread_->posted_task_count());
PostAfterStartupTaskFromBackgroundSequence(FROM_HERE, ui_thread_,
base::DoNothing());
PostAfterStartupTaskFromBackgroundSequence(FROM_HERE, background_sequence_,
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());
}
|