blob: 62a6fe16bbb1b6217ca2d0c5ceb04502b47a67cf [file] [log] [blame]
// Copyright 2017 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 "content/browser/background_fetch/background_fetch_scheduler.h"
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/guid.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/post_task.h"
#include "content/browser/background_fetch/background_fetch_job_controller.h"
#include "content/browser/background_fetch/background_fetch_request_info.h"
#include "content/browser/background_fetch/background_fetch_test_base.h"
#include "content/browser/background_fetch/background_fetch_test_data_manager.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
namespace content {
class FakeController : public BackgroundFetchJobController {
public:
FakeController(BackgroundFetchDataManager* data_manager,
BackgroundFetchDelegateProxy* delegate_proxy,
const BackgroundFetchRegistrationId& registration_id,
std::vector<std::string>* controller_sequence_list,
FinishedCallback finished_callback)
: BackgroundFetchJobController(
data_manager,
delegate_proxy,
registration_id,
blink::mojom::BackgroundFetchOptions::New(),
SkBitmap(),
/* bytes_downloaded= */ 0u,
/* bytes_uploaded= */ 0u,
/* upload_total= */ 0u,
base::DoNothing(),
std::move(finished_callback)),
controller_sequence_list_(controller_sequence_list) {
DCHECK(controller_sequence_list_);
}
~FakeController() override = default;
void DidCompleteRequest(
const std::string& guid,
std::unique_ptr<BackgroundFetchResult> result) override {
// Record the completed request. Store everything after the origin and the
// slash, to be able to directly compare with the provided requests.
controller_sequence_list_->push_back(
result->response->url_chain[0].path().substr(1));
// Continue normally.
BackgroundFetchJobController::DidCompleteRequest(guid, std::move(result));
}
private:
std::vector<std::string>* controller_sequence_list_;
};
class BackgroundFetchSchedulerTest : public BackgroundFetchTestBase {
public:
BackgroundFetchSchedulerTest() = default;
void SetUp() override {
BackgroundFetchTestBase::SetUp();
data_manager_ = std::make_unique<BackgroundFetchTestDataManager>(
browser_context(), storage_partition(),
embedded_worker_test_helper()->context_wrapper());
data_manager_->InitializeOnCoreThread();
delegate_proxy_ =
std::make_unique<BackgroundFetchDelegateProxy>(browser_context());
auto* background_fetch_context =
static_cast<StoragePartitionImpl*>(storage_partition())
->GetBackgroundFetchContext();
scheduler_ = std::make_unique<BackgroundFetchScheduler>(
background_fetch_context, data_manager_.get(), nullptr,
delegate_proxy_.get(), devtools_context().get(),
embedded_worker_test_helper()->context_wrapper());
}
void TearDown() override {
data_manager_ = nullptr;
delegate_proxy_ = nullptr;
scheduler_ = nullptr;
controller_sequence_list_.clear();
BackgroundFetchTestBase::TearDown();
}
protected:
void InitializeControllerWithRequests(
const url::Origin& fetch_origin,
const std::vector<std::string>& requests) {
std::vector<blink::mojom::FetchAPIRequestPtr> fetch_requests;
for (auto& request : requests) {
auto fetch_request = blink::mojom::FetchAPIRequest::New();
fetch_request->referrer = blink::mojom::Referrer::New();
fetch_request->url = GURL(fetch_origin.GetURL().spec() + request);
CreateRequestWithProvidedResponse(fetch_request->method,
fetch_request->url,
TestResponseBuilder(200).Build());
fetch_requests.push_back(std::move(fetch_request));
}
int64_t sw_id = RegisterServiceWorkerForOrigin(fetch_origin);
BackgroundFetchRegistrationId registration_id(
sw_id, fetch_origin, base::GenerateGUID(), base::GenerateGUID());
data_manager_->CreateRegistration(
registration_id, std::move(fetch_requests),
blink::mojom::BackgroundFetchOptions::New(), SkBitmap(),
/* start_paused= */ false, base::DoNothing());
task_environment_.RunUntilIdle();
auto controller = std::make_unique<FakeController>(
data_manager_.get(), delegate_proxy_.get(), registration_id,
&controller_sequence_list_,
base::BindOnce(&BackgroundFetchSchedulerTest::DidJobFinish,
base::Unretained(this)));
controller->InitializeRequestStatus(/* completed_downloads= */ 0,
requests.size(),
/* active_fetch_requests= */ {},
/* start_paused= */ false);
scheduler_->job_controllers_[registration_id.unique_id()] =
std::move(controller);
scheduler_->controller_ids_.push_back(registration_id);
}
void RunSchedulerToCompletion() {
scheduler_->ScheduleDownload();
task_environment_.RunUntilIdle();
}
void DidJobFinish(
const BackgroundFetchRegistrationId& registration_id,
blink::mojom::BackgroundFetchFailureReason failure_reason,
base::OnceCallback<void(blink::mojom::BackgroundFetchError)> callback) {
DCHECK_EQ(failure_reason, blink::mojom::BackgroundFetchFailureReason::NONE);
base::EraseIf(scheduler_->active_controllers_,
[&registration_id](auto* controller) {
return controller->registration_id() == registration_id;
});
scheduler_->job_controllers_.erase(registration_id.unique_id());
--scheduler_->num_active_registrations_;
scheduler_->ScheduleDownload();
}
protected:
void MakeSchedulerSequential() {
scheduler_->max_running_downloads_ = 1;
scheduler_->max_active_registrations_ = 1;
}
void MakeSchedulerConcurrent() {
scheduler_->max_running_downloads_ = 2;
scheduler_->max_active_registrations_ = 2;
}
std::vector<std::string> controller_sequence_list_;
private:
std::unique_ptr<BackgroundFetchDelegateProxy> delegate_proxy_;
std::unique_ptr<BackgroundFetchTestDataManager> data_manager_;
std::unique_ptr<BackgroundFetchScheduler> scheduler_;
};
TEST_F(BackgroundFetchSchedulerTest, SingleControllerSynchronous) {
MakeSchedulerSequential();
std::vector<std::string> requests = {"A1", "A2", "A3", "A4"};
InitializeControllerWithRequests(origin(), requests);
RunSchedulerToCompletion();
EXPECT_EQ(requests, controller_sequence_list_);
}
TEST_F(BackgroundFetchSchedulerTest, SingleControllerConcurrent) {
MakeSchedulerConcurrent();
std::vector<std::string> requests = {"A1", "A2", "A3", "A4"};
InitializeControllerWithRequests(origin(), requests);
RunSchedulerToCompletion();
EXPECT_EQ(requests, controller_sequence_list_);
}
TEST_F(BackgroundFetchSchedulerTest, TwoControllersSynchronous) {
MakeSchedulerSequential();
std::vector<std::string> all_requests = {"A1", "A2", "A3", "A4",
"B1", "B2", "B3"};
// Create a controller with A1 -> A4.
InitializeControllerWithRequests(
url::Origin::Create(GURL("https://A.com")),
std::vector<std::string>(all_requests.begin(), all_requests.begin() + 4));
// Create a controller with B1 -> B4.
InitializeControllerWithRequests(
url::Origin::Create(GURL("https://B.com")),
std::vector<std::string>(all_requests.begin() + 4, all_requests.end()));
RunSchedulerToCompletion();
EXPECT_EQ(all_requests, controller_sequence_list_);
}
TEST_F(BackgroundFetchSchedulerTest, TwoControllersConcurrent) {
MakeSchedulerConcurrent();
std::vector<std::string> all_requests = {"A1", "A2", "A3", "A4",
"B1", "B2", "B3"};
// Create a controller with A1 -> A4.
InitializeControllerWithRequests(
url::Origin::Create(GURL("https://A.com")),
std::vector<std::string>(all_requests.begin(), all_requests.begin() + 4));
// Create a controller with B1 -> B4.
InitializeControllerWithRequests(
url::Origin::Create(GURL("https://B.com")),
std::vector<std::string>(all_requests.begin() + 4, all_requests.end()));
RunSchedulerToCompletion();
std::vector<std::string> expected_sequence_list = {"A1", "B1", "A2", "B2",
"A3", "B3", "A4"};
EXPECT_EQ(expected_sequence_list, controller_sequence_list_);
}
TEST_F(BackgroundFetchSchedulerTest, TwoControllersConcurrentSameOrigin) {
MakeSchedulerConcurrent();
std::vector<std::string> all_requests = {"A1", "A2", "A3", "A4",
"B1", "B2", "B3"};
// Create a controller with A1 -> A4.
InitializeControllerWithRequests(
origin(),
std::vector<std::string>(all_requests.begin(), all_requests.begin() + 4));
// Create a controller with B1 -> B4.
InitializeControllerWithRequests(
origin(),
std::vector<std::string>(all_requests.begin() + 4, all_requests.end()));
RunSchedulerToCompletion();
EXPECT_EQ(all_requests, controller_sequence_list_);
}
} // namespace content