blob: c95c1c63226b9ebd734b03f8e41b2f9dffaaf40c [file] [log] [blame]
// Copyright 2019 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/background_sync/background_sync_scheduler.h"
#include <map>
#include <vector>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/hash/hash.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/browser/background_sync/background_sync_scheduler.h"
#include "content/browser/storage_partition_impl.h"
#include "content/common/content_export.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "content/test/mock_background_sync_controller.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace content {
namespace {
const char kUrl_1[] = "https://example.com";
const char kUrl_2[] = "https://whereswaldo.com";
class TestBrowserClient : public ContentBrowserClient {
public:
TestBrowserClient() = default;
~TestBrowserClient() override = default;
StoragePartitionConfig GetStoragePartitionConfigForSite(
BrowserContext* browser_context,
const GURL& site) override {
// The URL spec contains invalid filesystem characters like ':' so fasthash
// it to make it valid on all OS.
const auto spec_hash = base::NumberToString(base::FastHash(site.spec()));
return content::StoragePartitionConfig::Create(
browser_context, "PartitionDomain" + spec_hash, "Partition" + spec_hash,
false /* in_memory */);
}
};
} // namespace
class BackgroundSyncSchedulerTest : public testing::Test {
public:
BackgroundSyncSchedulerTest()
: task_environment_(BrowserTaskEnvironment::MainThreadType::UI) {}
void ScheduleDelayedProcessing(const GURL& url,
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay,
base::OnceClosure delayed_task) {
auto* scheduler = BackgroundSyncScheduler::GetFor(&test_browser_context_);
DCHECK(scheduler);
auto* storage_partition = static_cast<StoragePartitionImpl*>(
test_browser_context_.GetStoragePartitionForUrl(url));
DCHECK(storage_partition);
scheduler->ScheduleDelayedProcessing(storage_partition, sync_type, delay,
std::move(delayed_task));
}
void CancelDelayedProcessing(const GURL& url,
blink::mojom::BackgroundSyncType sync_type) {
auto* scheduler = BackgroundSyncScheduler::GetFor(&test_browser_context_);
DCHECK(scheduler);
auto* storage_partition = static_cast<StoragePartitionImpl*>(
test_browser_context_.GetStoragePartitionForUrl(url));
DCHECK(storage_partition);
scheduler->CancelDelayedProcessing(storage_partition, sync_type);
}
MockBackgroundSyncController* GetController() {
return static_cast<MockBackgroundSyncController*>(
test_browser_context_.GetBackgroundSyncController());
}
base::TimeDelta GetBrowserWakeupDelay(
blink::mojom::BackgroundSyncType sync_type) {
return GetController()->GetBrowserWakeupDelay(sync_type);
}
base::TimeTicks GetBrowserWakeupTime() {
auto* scheduler = BackgroundSyncScheduler::GetFor(&test_browser_context_);
DCHECK(scheduler);
return scheduler
->scheduled_wakeup_time_[blink::mojom::BackgroundSyncType::ONE_SHOT];
}
void SetUp() override {
original_client_ = SetBrowserClientForTesting(&browser_client_);
}
void TearDown() override { SetBrowserClientForTesting(original_client_); }
protected:
BrowserTaskEnvironment task_environment_;
TestBrowserClient browser_client_;
raw_ptr<ContentBrowserClient> original_client_;
TestBrowserContext test_browser_context_;
};
TEST_F(BackgroundSyncSchedulerTest, ScheduleInvokesCallback) {
base::RunLoop run_loop;
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Milliseconds(1), run_loop.QuitClosure());
run_loop.Run();
}
TEST_F(BackgroundSyncSchedulerTest, ZeroDelayScheduleDoesNotInvokeCallback) {
bool was_called = false;
ScheduleDelayedProcessing(
GURL(kUrl_1), blink::mojom::BackgroundSyncType::ONE_SHOT,
base::TimeDelta(),
base::BindOnce([](bool* was_called) { *was_called = true; },
&was_called));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(was_called);
}
TEST_F(BackgroundSyncSchedulerTest, CancelDoesNotInvokeCallback) {
bool was_called = false;
ScheduleDelayedProcessing(
GURL(kUrl_1), blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Minutes(1),
base::BindOnce([](bool* was_called) { *was_called = true; },
&was_called));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(was_called);
CancelDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::ONE_SHOT);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(was_called);
}
TEST_F(BackgroundSyncSchedulerTest, SchedulingTwiceOverwritesTimer) {
bool was_called = false;
ScheduleDelayedProcessing(
GURL(kUrl_1), blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Seconds(1),
base::BindOnce([](bool* was_called) { *was_called = true; },
&was_called));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(was_called);
base::RunLoop run_loop;
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Milliseconds(1), run_loop.QuitClosure());
run_loop.Run();
EXPECT_FALSE(was_called);
}
TEST_F(BackgroundSyncSchedulerTest, MultipleStoragePartitions) {
base::RunLoop run_loop_1, run_loop_2;
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Seconds(1), run_loop_1.QuitClosure());
ScheduleDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Milliseconds(1), run_loop_2.QuitClosure());
run_loop_1.Run();
run_loop_2.Run();
}
TEST_F(BackgroundSyncSchedulerTest, ScheduleBothTypesOfSync) {
base::RunLoop run_loop_1, run_loop_2;
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Milliseconds(1), run_loop_1.QuitClosure());
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC,
base::Milliseconds(1), run_loop_2.QuitClosure());
run_loop_1.Run();
run_loop_2.Run();
}
#if BUILDFLAG(IS_ANDROID)
TEST_F(BackgroundSyncSchedulerTest, BrowserWakeupScheduled) {
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Seconds(1), base::DoNothing());
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::Seconds(1));
ScheduleDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Milliseconds(1), base::DoNothing());
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::Milliseconds(1));
}
TEST_F(BackgroundSyncSchedulerTest,
BrowserWakeupScheduleSecondAfterFirstFinishes) {
base::RunLoop run_loop_1;
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Milliseconds(1), run_loop_1.QuitClosure());
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::Milliseconds(1));
run_loop_1.Run();
ScheduleDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Minutes(1), base::DoNothing());
EXPECT_GT(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::Milliseconds(1));
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::Minutes(1));
}
TEST_F(BackgroundSyncSchedulerTest, BrowserWakeupScheduleOneOfEachType) {
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC,
base::Seconds(1), base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::PERIODIC),
base::Seconds(1));
ScheduleDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Minutes(1), base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::Minutes(1));
}
TEST_F(BackgroundSyncSchedulerTest, BrowserWakeupScheduleThenCancel) {
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC,
base::Minutes(1), base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::PERIODIC),
base::Minutes(1));
CancelDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::PERIODIC),
base::TimeDelta::Max());
}
TEST_F(BackgroundSyncSchedulerTest, CancelingOneTypeDoesNotAffectAnother) {
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC,
base::Minutes(1), base::DoNothing());
ScheduleDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Seconds(1), base::DoNothing());
CancelDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::PERIODIC),
base::TimeDelta::Max());
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::Seconds(1));
}
TEST_F(BackgroundSyncSchedulerTest,
CancelingProcessingForOneStorageParitionUpdatesBrowserWakeup) {
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Minutes(1), base::DoNothing());
ScheduleDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::Seconds(1), base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::Seconds(1));
auto wakeup_time1 = GetBrowserWakeupTime();
CancelDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT);
base::RunLoop().RunUntilIdle();
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::Minutes(1));
EXPECT_GT(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::Seconds(1));
EXPECT_LT(wakeup_time1, GetBrowserWakeupTime());
}
#endif
} // namespace content