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 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/commit_deferring_condition_runner.h"
#include "content/public/browser/commit_deferring_condition.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/test_renderer_host.h"
#include "content/test/mock_commit_deferring_condition.h"
namespace content {
class CommitDeferringConditionRunnerTest
: public RenderViewHostTestHarness,
public CommitDeferringConditionRunner::Delegate {
public:
CommitDeferringConditionRunnerTest() = default;
void SetUp() override {
RenderViewHostTestHarness::SetUp();
runner_ = base::WrapUnique(new CommitDeferringConditionRunner(
*this, CommitDeferringCondition::NavigationType::kOther,
/*candidate_prerender_frame_tree_node_id=*/std::nullopt));
}
// Whether the callback was called.
bool was_delegate_notified() const { return was_delegate_notified_; }
bool is_deferring() { return runner_->is_deferred_; }
CommitDeferringConditionRunner* runner() { return runner_.get(); }
private:
// CommitDeferringConditionRunner::Delegate:
void OnCommitDeferringConditionChecksComplete(
CommitDeferringCondition::NavigationType navigation_type,
std::optional<FrameTreeNodeId> candidate_prerender_frame_tree_node_id)
override {
EXPECT_EQ(navigation_type,
CommitDeferringCondition::NavigationType::kOther);
EXPECT_FALSE(candidate_prerender_frame_tree_node_id.has_value());
was_delegate_notified_ = true;
}
std::unique_ptr<CommitDeferringConditionRunner> runner_;
bool was_delegate_notified_ = false;
};
// CommitDeferringCondition always need a NavigationHandle. Since we don't have
// a navigation here, this class is just used to provide it with a
// MockNavigationHandle.
class MockHandleConditionWrapper : public MockNavigationHandle,
public MockCommitDeferringConditionWrapper {
public:
explicit MockHandleConditionWrapper(CommitDeferringCondition::Result result)
: MockCommitDeferringConditionWrapper(*this, result) {}
};
// Check that the runner notifies the delegate synchronously when there are no
// conditions registered.
TEST_F(CommitDeferringConditionRunnerTest, NoRegisteredConditions) {
EXPECT_FALSE(was_delegate_notified());
runner()->ProcessChecks();
EXPECT_TRUE(was_delegate_notified());
}
// Test that when a condition defers asynchronously, the delegate isn't
// notified until the condition signals completion.
TEST_F(CommitDeferringConditionRunnerTest, BasicAsync) {
MockHandleConditionWrapper condition(
CommitDeferringCondition::Result::kDefer);
runner()->AddConditionForTesting(condition.PassToDelegate());
runner()->ProcessChecks();
EXPECT_FALSE(was_delegate_notified());
EXPECT_TRUE(condition.WasInvoked());
EXPECT_TRUE(is_deferring());
condition.CallResumeClosure();
EXPECT_TRUE(was_delegate_notified());
}
// Test that if a condition is already satisfied when ProcessChecks is
// called, the delegate is notified synchronously.
TEST_F(CommitDeferringConditionRunnerTest, BasicSync) {
MockHandleConditionWrapper condition(
CommitDeferringCondition::Result::kProceed);
runner()->AddConditionForTesting(condition.PassToDelegate());
runner()->ProcessChecks();
EXPECT_TRUE(was_delegate_notified());
EXPECT_TRUE(condition.WasInvoked());
}
// Test that if a condition indicating the cancellation of the commit,
// the delegate is not notified.
TEST_F(CommitDeferringConditionRunnerTest, BasicCancelled) {
MockHandleConditionWrapper condition(
CommitDeferringCondition::Result::kCancelled);
runner()->AddConditionForTesting(condition.PassToDelegate());
runner()->ProcessChecks();
EXPECT_FALSE(was_delegate_notified());
EXPECT_TRUE(condition.WasInvoked());
}
// Test with multiple conditions, some of which are completed synchronously and
// some asynchronously. The final condition is asynchronous and should notify
// the delegate on resumption.
TEST_F(CommitDeferringConditionRunnerTest, MultipleConditionsLastAsync) {
// Add conditions, alternating between those that are already satisfied at
// ProcessChecks time and those that complete asynchronously.
// Complete -> Async -> Complete -> Async
MockHandleConditionWrapper condition1(
CommitDeferringCondition::Result::kProceed);
runner()->AddConditionForTesting(condition1.PassToDelegate());
MockHandleConditionWrapper condition2(
CommitDeferringCondition::Result::kDefer);
runner()->AddConditionForTesting(condition2.PassToDelegate());
MockHandleConditionWrapper condition3(
CommitDeferringCondition::Result::kProceed);
runner()->AddConditionForTesting(condition3.PassToDelegate());
MockHandleConditionWrapper condition4(
CommitDeferringCondition::Result::kDefer);
runner()->AddConditionForTesting(condition4.PassToDelegate());
runner()->ProcessChecks();
// The first should have been completed synchronously so we should have
// invoked the second condition and are waiting on it now.
EXPECT_FALSE(was_delegate_notified());
EXPECT_TRUE(condition1.WasInvoked());
EXPECT_TRUE(condition2.WasInvoked());
EXPECT_FALSE(condition3.WasInvoked());
EXPECT_FALSE(condition4.WasInvoked());
EXPECT_TRUE(is_deferring());
// Complete the second condition. The third is already completed so we should
// synchronously skip to the fourth.
condition2.CallResumeClosure();
EXPECT_FALSE(was_delegate_notified());
EXPECT_TRUE(condition3.WasInvoked());
EXPECT_TRUE(condition4.WasInvoked());
EXPECT_TRUE(is_deferring());
// Completing the final condition should notify the delegate.
condition4.CallResumeClosure();
EXPECT_TRUE(was_delegate_notified());
EXPECT_FALSE(is_deferring());
}
// Test with multiple conditions, some of which are completed synchronously and
// some asynchronously. The final condition is synchronous and should notify
// the delegate synchronously from resuming the last asynchronous condition.
TEST_F(CommitDeferringConditionRunnerTest, MultipleConditionsLastSync) {
// Add conditions, alternating between those that are already satisfied at
// ProcessChecks time and those that complete asynchronously.
// Async -> Complete -> Async -> Complete
MockHandleConditionWrapper condition1(
CommitDeferringCondition::Result::kDefer);
runner()->AddConditionForTesting(condition1.PassToDelegate());
MockHandleConditionWrapper condition2(
CommitDeferringCondition::Result::kProceed);
runner()->AddConditionForTesting(condition2.PassToDelegate());
MockHandleConditionWrapper condition3(
CommitDeferringCondition::Result::kDefer);
runner()->AddConditionForTesting(condition3.PassToDelegate());
MockHandleConditionWrapper condition4(
CommitDeferringCondition::Result::kProceed);
runner()->AddConditionForTesting(condition4.PassToDelegate());
runner()->ProcessChecks();
// The first condition is deferring asynchronously.
EXPECT_FALSE(was_delegate_notified());
EXPECT_TRUE(condition1.WasInvoked());
EXPECT_FALSE(condition2.WasInvoked());
EXPECT_TRUE(is_deferring());
// Complete the first condition. The second is a synchronous condition so we
// should now be awaiting completion of the third which is async.
condition1.CallResumeClosure();
EXPECT_FALSE(was_delegate_notified());
EXPECT_TRUE(condition2.WasInvoked());
EXPECT_TRUE(condition3.WasInvoked());
EXPECT_FALSE(condition4.WasInvoked());
EXPECT_TRUE(is_deferring());
// Resuming from the third condition should synchronously complete the fourth
// and then notify the delegate.
condition3.CallResumeClosure();
EXPECT_TRUE(condition4.WasInvoked());
EXPECT_TRUE(was_delegate_notified());
EXPECT_FALSE(is_deferring());
}
// Test with multiple conditions, with one indicating that the commit is
// cancelled invoked in the middle.
TEST_F(CommitDeferringConditionRunnerTest, MultipleConditionsWithCancelled) {
MockHandleConditionWrapper condition1(
CommitDeferringCondition::Result::kProceed);
runner()->AddConditionForTesting(condition1.PassToDelegate());
MockHandleConditionWrapper condition2(
CommitDeferringCondition::Result::kCancelled);
runner()->AddConditionForTesting(condition2.PassToDelegate());
MockHandleConditionWrapper condition3(
CommitDeferringCondition::Result::kProceed);
runner()->AddConditionForTesting(condition3.PassToDelegate());
runner()->ProcessChecks();
// Only the first two conditions are invoked, as the commit is cancelled with
// the second condition.
EXPECT_FALSE(was_delegate_notified());
EXPECT_TRUE(condition1.WasInvoked());
EXPECT_TRUE(condition2.WasInvoked());
EXPECT_FALSE(condition3.WasInvoked());
EXPECT_FALSE(is_deferring());
}
} // namespace content
|