blob: 0e3c32cb08d5c932f1c9b4658471e7d2618a8f8d [file] [log] [blame]
// Copyright (c) 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 "components/browsing_data/core/counters/history_counter.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/run_loop.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/history/web_history_service_factory.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/browser/ui/browser.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/browsing_data/core/browsing_data_utils.h"
#include "components/browsing_data/core/pref_names.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/web_history_service.h"
#include "components/history/core/test/fake_web_history_service.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/profile_oauth2_token_service.h"
#include "components/signin/core/browser/signin_manager.h"
#include "net/http/http_status_code.h"
#include "url/gurl.h"
namespace {
class HistoryCounterTest : public SyncTest {
public:
HistoryCounterTest() : SyncTest(SINGLE_CLIENT) {
// TODO(msramek): Only one of the test cases, RestartOnSyncChange, is a Sync
// integration test. Extract it and move it to the rest of integration tests
// in chrome/browser/sync/test/integration/. Change this class back to
// InProcessBrowserTest.
}
~HistoryCounterTest() override {};
void SetUpOnMainThread() override {
time_ = base::Time::Now();
history_service_ = HistoryServiceFactory::GetForProfileWithoutCreating(
browser()->profile());
fake_web_history_service_.reset(new history::FakeWebHistoryService(
ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile()),
SigninManagerFactory::GetForProfile(browser()->profile()),
browser()->profile()->GetRequestContext()));
SetHistoryDeletionPref(true);
SetDeletionPeriodPref(browsing_data::ALL_TIME);
}
void AddVisit(const std::string url) {
history_service_->AddPage(GURL(url), time_, history::SOURCE_BROWSED);
}
const base::Time& GetCurrentTime() {
return time_;
}
void RevertTimeInDays(int days) {
time_ -= base::TimeDelta::FromDays(days);
}
void SetHistoryDeletionPref(bool value) {
browser()->profile()->GetPrefs()->SetBoolean(
browsing_data::prefs::kDeleteBrowsingHistory, value);
}
void SetDeletionPeriodPref(browsing_data::TimePeriod period) {
browser()->profile()->GetPrefs()->SetInteger(
browsing_data::prefs::kDeleteTimePeriod, static_cast<int>(period));
}
void WaitForCounting() {
run_loop_.reset(new base::RunLoop());
run_loop_->Run();
}
browsing_data::BrowsingDataCounter::ResultInt GetLocalResult() {
DCHECK(finished_);
return local_result_;
}
bool HasSyncedVisits() {
DCHECK(finished_);
return has_synced_visits_;
}
void Callback(
std::unique_ptr<browsing_data::BrowsingDataCounter::Result> result) {
finished_ = result->Finished();
if (finished_) {
browsing_data::HistoryCounter::HistoryResult* history_result =
static_cast<browsing_data::HistoryCounter::HistoryResult*>(
result.get());
local_result_ = history_result->Value();
has_synced_visits_ = history_result->has_synced_visits();
}
if (run_loop_ && finished_)
run_loop_->Quit();
}
bool CountingFinishedSinceLastAsked() {
bool result = finished_;
finished_ = false;
return result;
}
void WaitForCountingOrConfirmFinished() {
if (CountingFinishedSinceLastAsked())
return;
WaitForCounting();
CountingFinishedSinceLastAsked();
}
history::WebHistoryService* GetFakeWebHistoryService(Profile* profile,
bool check_sync_status) {
// |check_sync_status| is true when the factory should check if
// history sync is enabled.
if (!check_sync_status ||
WebHistoryServiceFactory::GetForProfile(profile)) {
return fake_web_history_service_.get();
}
return nullptr;
}
history::WebHistoryService* GetRealWebHistoryService(Profile* profile) {
return WebHistoryServiceFactory::GetForProfile(profile);
}
history::HistoryService* GetHistoryService() { return history_service_; }
private:
std::unique_ptr<base::RunLoop> run_loop_;
history::HistoryService* history_service_;
std::unique_ptr<history::FakeWebHistoryService> fake_web_history_service_;
base::Time time_;
bool finished_;
browsing_data::BrowsingDataCounter::ResultInt local_result_;
bool has_synced_visits_;
};
// Tests that the counter considers duplicate visits from the same day
// to be a single item.
IN_PROC_BROWSER_TEST_F(HistoryCounterTest, DuplicateVisits) {
AddVisit("https://www.google.com"); // 1 item
AddVisit("https://www.google.com");
AddVisit("https://www.chrome.com"); // 2 items
AddVisit("https://www.chrome.com");
AddVisit("https://www.chrome.com");
AddVisit("https://www.example.com"); // 3 items
RevertTimeInDays(1);
AddVisit("https://www.google.com"); // 4 items
AddVisit("https://www.example.com"); // 5 items
AddVisit("https://www.example.com");
RevertTimeInDays(1);
AddVisit("https://www.chrome.com"); // 6 items
AddVisit("https://www.chrome.com");
AddVisit("https://www.google.com"); // 7 items
AddVisit("https://www.chrome.com");
AddVisit("https://www.google.com");
AddVisit("https://www.google.com");
AddVisit("https://www.chrome.com");
Profile* profile = browser()->profile();
browsing_data::HistoryCounter counter(
GetHistoryService(),
base::Bind(&HistoryCounterTest::GetRealWebHistoryService,
base::Unretained(this),
base::Unretained(profile)),
ProfileSyncServiceFactory::GetForProfile(profile));
counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
base::Unretained(this)));
counter.Restart();
WaitForCounting();
EXPECT_EQ(7u, GetLocalResult());
}
// Tests that the counter starts counting automatically when the deletion
// pref changes to true.
IN_PROC_BROWSER_TEST_F(HistoryCounterTest, PrefChanged) {
SetHistoryDeletionPref(false);
AddVisit("https://www.google.com");
AddVisit("https://www.chrome.com");
Profile* profile = browser()->profile();
browsing_data::HistoryCounter counter(
GetHistoryService(),
base::Bind(&HistoryCounterTest::GetRealWebHistoryService,
base::Unretained(this),
base::Unretained(profile)),
ProfileSyncServiceFactory::GetForProfile(profile));
counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
base::Unretained(this)));
SetHistoryDeletionPref(true);
WaitForCounting();
EXPECT_EQ(2u, GetLocalResult());
}
// Tests that the counter does not count history if the deletion
// preference is false.
IN_PROC_BROWSER_TEST_F(HistoryCounterTest, PrefIsFalse) {
SetHistoryDeletionPref(false);
AddVisit("https://www.google.com");
Profile* profile = browser()->profile();
browsing_data::HistoryCounter counter(
GetHistoryService(),
base::Bind(&HistoryCounterTest::GetRealWebHistoryService,
base::Unretained(this),
base::Unretained(profile)),
ProfileSyncServiceFactory::GetForProfile(profile));
counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
base::Unretained(this)));
counter.Restart();
EXPECT_FALSE(counter.HasTrackedTasks());
}
// Tests that changing the deletion period restarts the counting, and that
// the result takes visit dates into account.
IN_PROC_BROWSER_TEST_F(HistoryCounterTest, PeriodChanged) {
AddVisit("https://www.google.com");
RevertTimeInDays(2);
AddVisit("https://www.google.com");
AddVisit("https://www.example.com");
RevertTimeInDays(4);
AddVisit("https://www.chrome.com");
AddVisit("https://www.chrome.com");
AddVisit("https://www.example.com");
RevertTimeInDays(20);
AddVisit("https://www.google.com");
AddVisit("https://www.chrome.com");
AddVisit("https://www.example.com");
RevertTimeInDays(10);
AddVisit("https://www.example.com");
AddVisit("https://www.example.com");
AddVisit("https://www.example.com");
Profile* profile = browser()->profile();
browsing_data::HistoryCounter counter(
GetHistoryService(),
base::Bind(&HistoryCounterTest::GetRealWebHistoryService,
base::Unretained(this),
base::Unretained(profile)),
ProfileSyncServiceFactory::GetForProfile(profile));
counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
base::Unretained(this)));
SetDeletionPeriodPref(browsing_data::LAST_HOUR);
WaitForCounting();
EXPECT_EQ(1u, GetLocalResult());
SetDeletionPeriodPref(browsing_data::LAST_DAY);
WaitForCounting();
EXPECT_EQ(1u, GetLocalResult());
SetDeletionPeriodPref(browsing_data::LAST_WEEK);
WaitForCounting();
EXPECT_EQ(5u, GetLocalResult());
SetDeletionPeriodPref(browsing_data::FOUR_WEEKS);
WaitForCounting();
EXPECT_EQ(8u, GetLocalResult());
SetDeletionPeriodPref(browsing_data::ALL_TIME);
WaitForCounting();
EXPECT_EQ(9u, GetLocalResult());
}
// Test the behavior for a profile that syncs history.
IN_PROC_BROWSER_TEST_F(HistoryCounterTest, Synced) {
// WebHistoryService makes network requests, so we need to use a fake one
// for testing.
Profile* profile = browser()->profile();
browsing_data::HistoryCounter counter(
GetHistoryService(),
base::Bind(&HistoryCounterTest::GetFakeWebHistoryService,
base::Unretained(this),
base::Unretained(profile),
false),
ProfileSyncServiceFactory::GetForProfile(profile));
counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
base::Unretained(this)));
history::FakeWebHistoryService* service =
static_cast<history::FakeWebHistoryService*>(GetFakeWebHistoryService(
profile, false));
// No entries locally and no entries in Sync.
service->SetupFakeResponse(true /* success */, net::HTTP_OK);
counter.Restart();
WaitForCounting();
EXPECT_EQ(0u, GetLocalResult());
EXPECT_FALSE(HasSyncedVisits());
// No entries locally. There are some entries in Sync, but they are out of the
// time range.
SetDeletionPeriodPref(browsing_data::LAST_HOUR);
service->AddSyncedVisit(
"www.google.com", GetCurrentTime() - base::TimeDelta::FromHours(2));
service->AddSyncedVisit(
"www.chrome.com", GetCurrentTime() - base::TimeDelta::FromHours(2));
service->SetupFakeResponse(true /* success */, net::HTTP_OK);
counter.Restart();
WaitForCounting();
EXPECT_EQ(0u, GetLocalResult());
EXPECT_FALSE(HasSyncedVisits());
// No entries locally, but some entries in Sync.
service->AddSyncedVisit("www.google.com", GetCurrentTime());
service->SetupFakeResponse(true /* success */, net::HTTP_OK);
counter.Restart();
WaitForCounting();
EXPECT_EQ(0u, GetLocalResult());
EXPECT_TRUE(HasSyncedVisits());
// To err on the safe side, if the server request fails, we assume that there
// might be some items on the server.
service->SetupFakeResponse(true /* success */,
net::HTTP_INTERNAL_SERVER_ERROR);
counter.Restart();
WaitForCounting();
EXPECT_EQ(0u, GetLocalResult());
EXPECT_TRUE(HasSyncedVisits());
// Same when the entire query fails.
service->SetupFakeResponse(false /* success */,
net::HTTP_INTERNAL_SERVER_ERROR);
counter.Restart();
WaitForCounting();
EXPECT_EQ(0u, GetLocalResult());
EXPECT_TRUE(HasSyncedVisits());
// Nonzero local count, nonempty sync.
AddVisit("https://www.google.com");
AddVisit("https://www.chrome.com");
service->SetupFakeResponse(true /* success */, net::HTTP_OK);
counter.Restart();
WaitForCounting();
EXPECT_EQ(2u, GetLocalResult());
EXPECT_TRUE(HasSyncedVisits());
// Nonzero local count, empty sync.
service->ClearSyncedVisits();
service->SetupFakeResponse(true /* success */, net::HTTP_OK);
counter.Restart();
WaitForCounting();
EXPECT_EQ(2u, GetLocalResult());
EXPECT_FALSE(HasSyncedVisits());
}
// Test that the counting restarts when history sync state changes.
// TODO(crbug.com/553421): Enable this test and move it to the
// sync/test/integration directory.
IN_PROC_BROWSER_TEST_F(HistoryCounterTest, DISABLED_RestartOnSyncChange) {
// Set up the Sync client.
ASSERT_TRUE(SetupClients());
static const int kFirstProfileIndex = 0;
browser_sync::ProfileSyncService* sync_service =
GetSyncService(kFirstProfileIndex);
Profile* profile = GetProfile(kFirstProfileIndex);
// Set up the fake web history service and the counter.
browsing_data::HistoryCounter counter(
GetHistoryService(),
base::Bind(&HistoryCounterTest::GetFakeWebHistoryService,
base::Unretained(this),
base::Unretained(profile),
true),
sync_service);
counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
base::Unretained(this)));
// Note that some Sync operations notify observers immediately (and thus there
// is no need to call |WaitForCounting()|; in fact, it would block the test),
// while other operations only post the task on UI thread's message loop
// (which requires calling |WaitForCounting()| for them to run). Therefore,
// this test always checks if the callback has already run and only waits
// if it has not.
// We sync all datatypes by default, so starting Sync means that we start
// syncing history deletion, and this should restart the counter.
ASSERT_TRUE(SetupSync());
ASSERT_TRUE(sync_service->IsSyncActive());
ASSERT_TRUE(sync_service->GetPreferredDataTypes().Has(
syncer::HISTORY_DELETE_DIRECTIVES));
WaitForCountingOrConfirmFinished();
// We stop syncing history deletion in particular. This restarts the counter.
syncer::ModelTypeSet everything_except_history = syncer::ModelTypeSet::All();
everything_except_history.Remove(syncer::HISTORY_DELETE_DIRECTIVES);
auto sync_blocker = sync_service->GetSetupInProgressHandle();
sync_service->ChangePreferredDataTypes(everything_except_history);
sync_blocker.reset();
WaitForCountingOrConfirmFinished();
// If the history deletion sync is not affected, the counter is not restarted.
syncer::ModelTypeSet only_passwords(syncer::PASSWORDS);
sync_service->ChangePreferredDataTypes(only_passwords);
sync_blocker = sync_service->GetSetupInProgressHandle();
sync_service->ChangePreferredDataTypes(only_passwords);
sync_blocker.reset();
EXPECT_FALSE(counter.HasTrackedTasks());
EXPECT_FALSE(CountingFinishedSinceLastAsked());
// Same in this case.
syncer::ModelTypeSet autofill_and_passwords(
syncer::AUTOFILL, syncer::PASSWORDS);
sync_blocker = sync_service->GetSetupInProgressHandle();
sync_service->ChangePreferredDataTypes(autofill_and_passwords);
sync_blocker.reset();
EXPECT_FALSE(counter.HasTrackedTasks());
EXPECT_FALSE(CountingFinishedSinceLastAsked());
// We start syncing history deletion again. This restarts the counter.
sync_blocker = sync_service->GetSetupInProgressHandle();
sync_service->ChangePreferredDataTypes(syncer::ModelTypeSet::All());
sync_blocker.reset();
WaitForCountingOrConfirmFinished();
// Changing the syncing datatypes to another set that still includes history
// deletion should technically not trigger a restart, because the state of
// history deletion did not change. However, in reality we can get two
// notifications, one that history sync has stopped and another that it is
// active again.
// Stopping the Sync service triggers a restart.
sync_service->RequestStop(syncer::SyncService::CLEAR_DATA);
WaitForCountingOrConfirmFinished();
}
} // namespace