blob: 0510fecdb8d09a7fcaaa691830d1da721936b27c [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/resource_coordinator/local_site_characteristics_data_store.h"
#include "base/macros.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h"
#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
#include "chrome/browser/resource_coordinator/tab_manager_features.h"
#include "chrome/browser/resource_coordinator/time.h"
#include "chrome/test/base/testing_profile.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/url_row.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace resource_coordinator {
namespace {
const url::Origin kTestOrigin = url::Origin::Create(GURL("http://www.foo.com"));
const url::Origin kTestOrigin2 =
url::Origin::Create(GURL("http://www.bar.com"));
constexpr base::TimeDelta kDelay = base::TimeDelta::FromMinutes(1);
class MockLocalSiteCharacteristicsDatabase
: public testing::NoopLocalSiteCharacteristicsDatabase {
public:
MockLocalSiteCharacteristicsDatabase() = default;
~MockLocalSiteCharacteristicsDatabase() = default;
MOCK_METHOD1(RemoveSiteCharacteristicsFromDB,
void(const std::vector<url::Origin>&));
MOCK_METHOD0(ClearDatabase, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockLocalSiteCharacteristicsDatabase);
};
} // namespace
class LocalSiteCharacteristicsDataStoreTest : public ::testing::Test {
protected:
LocalSiteCharacteristicsDataStoreTest()
: scoped_set_tick_clock_for_testing_(&test_clock_) {
scoped_feature_list_.InitAndEnableFeature(
features::kSiteCharacteristicsDatabase);
data_store_ =
std::make_unique<LocalSiteCharacteristicsDataStore>(&profile_);
mock_db_ =
new ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase>();
data_store_->SetDatabaseForTesting(base::WrapUnique(mock_db_));
test_clock_.SetNowTicks(base::TimeTicks::UnixEpoch());
test_clock_.Advance(base::TimeDelta::FromHours(1));
WaitForAsyncOperationsToComplete();
}
void TearDown() override { WaitForAsyncOperationsToComplete(); }
void WaitForAsyncOperationsToComplete() {
test_browser_thread_bundle_.RunUntilIdle();
}
// Populates |writer_|, |reader_| and |data_| to refer to a tab navigated to
// |kTestOrigin| that updated its title in background. Populates |writer2_|,
// |reader2_| and |data2_| to refer to a tab navigated to |kTestOrigin2| that
// updates its favicon in background.
void SetupTwoSitesUsingFeaturesInBackground() {
// Load a first origin, and then make use of a feature on it.
ASSERT_FALSE(reader_);
reader_ = data_store_->GetReaderForOrigin(kTestOrigin);
EXPECT_TRUE(reader_);
ASSERT_FALSE(writer_);
writer_ = data_store_->GetWriterForOrigin(kTestOrigin,
TabVisibility::kBackground);
EXPECT_TRUE(writer_);
ASSERT_FALSE(data_);
data_ =
data_store_->origin_data_map_for_testing().find(kTestOrigin)->second;
EXPECT_TRUE(data_);
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown,
reader_->UpdatesTitleInBackground());
writer_->NotifySiteLoaded();
writer_->NotifyUpdatesTitleInBackground();
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse,
reader_->UpdatesTitleInBackground());
test_clock_.Advance(kDelay);
// Load a second origin, make use of a feature on it too.
ASSERT_FALSE(reader2_);
reader2_ = data_store_->GetReaderForOrigin(kTestOrigin2);
EXPECT_TRUE(reader2_);
ASSERT_FALSE(writer2_);
writer2_ = data_store_->GetWriterForOrigin(kTestOrigin2,
TabVisibility::kBackground);
EXPECT_TRUE(writer2_);
ASSERT_FALSE(data2_);
data2_ =
data_store_->origin_data_map_for_testing().find(kTestOrigin2)->second;
EXPECT_TRUE(data2_);
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown,
reader2_->UpdatesFaviconInBackground());
writer2_->NotifySiteLoaded();
writer2_->NotifyUpdatesFaviconInBackground();
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse,
reader2_->UpdatesFaviconInBackground());
test_clock_.Advance(kDelay);
}
base::SimpleTestTickClock test_clock_;
ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_;
content::TestBrowserThreadBundle test_browser_thread_bundle_;
base::test::ScopedFeatureList scoped_feature_list_;
TestingProfile profile_;
// Owned by |data_store_|.
::testing::StrictMock<MockLocalSiteCharacteristicsDatabase>* mock_db_ =
nullptr;
std::unique_ptr<LocalSiteCharacteristicsDataStore> data_store_;
std::unique_ptr<SiteCharacteristicsDataReader> reader_;
std::unique_ptr<SiteCharacteristicsDataWriter> writer_;
internal::LocalSiteCharacteristicsDataImpl* data_ = nullptr;
std::unique_ptr<SiteCharacteristicsDataReader> reader2_;
std::unique_ptr<SiteCharacteristicsDataWriter> writer2_;
internal::LocalSiteCharacteristicsDataImpl* data2_ = nullptr;
};
TEST_F(LocalSiteCharacteristicsDataStoreTest, EndToEnd) {
auto reader = data_store_->GetReaderForOrigin(kTestOrigin);
EXPECT_TRUE(reader);
auto writer =
data_store_->GetWriterForOrigin(kTestOrigin, TabVisibility::kBackground);
EXPECT_TRUE(writer);
EXPECT_EQ(1U, data_store_->origin_data_map_for_testing().size());
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown,
reader->UpdatesTitleInBackground());
writer->NotifySiteLoaded();
writer->NotifyUpdatesTitleInBackground();
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse,
reader->UpdatesTitleInBackground());
writer->NotifySiteUnloaded();
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse,
reader->UpdatesTitleInBackground());
auto reader_copy = data_store_->GetReaderForOrigin(kTestOrigin);
EXPECT_EQ(1U, data_store_->origin_data_map_for_testing().size());
auto reader2 = data_store_->GetReaderForOrigin(kTestOrigin2);
EXPECT_EQ(2U, data_store_->origin_data_map_for_testing().size());
reader2.reset();
WaitForAsyncOperationsToComplete();
EXPECT_EQ(1U, data_store_->origin_data_map_for_testing().size());
reader_copy.reset();
reader.reset();
writer.reset();
EXPECT_TRUE(data_store_->origin_data_map_for_testing().empty());
EXPECT_CALL(*mock_db_, ClearDatabase());
data_store_->OnURLsDeleted(nullptr, history::DeletionInfo::ForAllHistory());
}
// Verify that an origin is removed from the data store (in memory and on disk)
// when there are no more references to it in the history, after the history is
// partially cleared.
TEST_F(LocalSiteCharacteristicsDataStoreTest,
OnURLsDeleted_Partial_OriginNotReferenced) {
SetupTwoSitesUsingFeaturesInBackground();
const base::TimeDelta last_loaded_time2_before_urls_deleted =
data2_->last_loaded_time_for_testing();
// Make sure that all data passed to |OnURLsDeleted| get passed to the
// database, even if they're not in the internal map used by the data store.
const url::Origin kOriginNotInMap =
url::Origin::Create(GURL("http://www.url-not-in-map.com"));
history::URLRows urls_to_delete = {history::URLRow(kTestOrigin.GetURL()),
history::URLRow(kOriginNotInMap.GetURL())};
history::DeletionInfo deletion_info =
history::DeletionInfo::ForUrls(urls_to_delete, std::set<GURL>());
deletion_info.set_deleted_urls_origin_map({
{kTestOrigin.GetURL(), {0, base::Time::Now()}},
{kOriginNotInMap.GetURL(), {0, base::Time::Now()}},
});
EXPECT_CALL(*mock_db_,
RemoveSiteCharacteristicsFromDB(::testing::WhenSorted(
::testing::ElementsAre(kTestOrigin, kOriginNotInMap))));
data_store_->OnURLsDeleted(nullptr, deletion_info);
::testing::Mock::VerifyAndClear(mock_db_);
// The information for the first site should have been cleared. The last
// loaded time should be equal to the current time.
EXPECT_EQ(data_->last_loaded_time_for_testing(),
test_clock_.NowTicks() - base::TimeTicks::UnixEpoch());
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown,
reader_->UpdatesTitleInBackground());
// The second site shouldn't have been cleared.
EXPECT_EQ(data2_->last_loaded_time_for_testing(),
last_loaded_time2_before_urls_deleted);
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse,
reader2_->UpdatesFaviconInBackground());
writer_->NotifySiteUnloaded();
writer2_->NotifySiteUnloaded();
}
// Verify that an origin is *not* removed from the data store (in memory and on
// disk) when there remain references to it in the history, after the history is
// partially cleared.
TEST_F(LocalSiteCharacteristicsDataStoreTest,
OnURLsDeleted_Partial_OriginStillReferenced) {
SetupTwoSitesUsingFeaturesInBackground();
const base::TimeDelta last_loaded_time_before_urls_deleted =
data_->last_loaded_time_for_testing();
const base::TimeDelta last_loaded_time2_before_urls_deleted =
data2_->last_loaded_time_for_testing();
// Make sure that all data passed to |OnURLsDeleted| get passed to the
// database, even if they're not in the internal map used by the data store.
const url::Origin kOriginNotInMap =
url::Origin::Create(GURL("http://www.url-not-in-map.com"));
history::URLRows urls_to_delete = {history::URLRow(kTestOrigin.GetURL()),
history::URLRow(kOriginNotInMap.GetURL())};
history::DeletionInfo deletion_info =
history::DeletionInfo::ForUrls(urls_to_delete, std::set<GURL>());
deletion_info.set_deleted_urls_origin_map({
{kTestOrigin.GetURL(), {4, base::Time::Now()}},
{kOriginNotInMap.GetURL(), {3, base::Time::Now()}},
});
data_store_->OnURLsDeleted(nullptr, deletion_info);
::testing::Mock::VerifyAndClear(mock_db_);
// Sites shouldn't have been cleared.
EXPECT_EQ(data_->last_loaded_time_for_testing(),
last_loaded_time_before_urls_deleted);
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse,
reader_->UpdatesTitleInBackground());
EXPECT_EQ(data2_->last_loaded_time_for_testing(),
last_loaded_time2_before_urls_deleted);
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse,
reader2_->UpdatesFaviconInBackground());
writer_->NotifySiteUnloaded();
writer2_->NotifySiteUnloaded();
}
// Verify that origins are removed from the data store (in memory and on disk)
// when the history is completely cleared.
TEST_F(LocalSiteCharacteristicsDataStoreTest, OnURLsDeleted_Full) {
SetupTwoSitesUsingFeaturesInBackground();
// Delete all the information stored in the data store.
EXPECT_CALL(*mock_db_, ClearDatabase());
data_store_->OnURLsDeleted(nullptr, history::DeletionInfo::ForAllHistory());
::testing::Mock::VerifyAndClear(mock_db_);
// The information for both sites should have been cleared.
EXPECT_EQ(data_->last_loaded_time_for_testing(),
test_clock_.NowTicks() - base::TimeTicks::UnixEpoch());
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown,
reader_->UpdatesTitleInBackground());
EXPECT_EQ(data2_->last_loaded_time_for_testing(),
test_clock_.NowTicks() - base::TimeTicks::UnixEpoch());
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown,
reader2_->UpdatesFaviconInBackground());
writer_->NotifySiteUnloaded();
writer2_->NotifySiteUnloaded();
}
TEST_F(LocalSiteCharacteristicsDataStoreTest, InspectorWorks) {
// Make sure the inspector interface was registered at construction.
LocalSiteCharacteristicsDataStoreInspector* inspector =
LocalSiteCharacteristicsDataStoreInspector::GetForProfile(&profile_);
EXPECT_NE(nullptr, inspector);
EXPECT_EQ(data_store_.get(), inspector);
EXPECT_STREQ("LocalSiteCharacteristicsDataStore",
inspector->GetDataStoreName());
// We expect an empty data store at the outset.
EXPECT_EQ(0U, inspector->GetAllInMemoryOrigins().size());
std::unique_ptr<SiteCharacteristicsProto> data;
bool is_dirty = false;
EXPECT_FALSE(inspector->GetDataForOrigin(kTestOrigin, &is_dirty, &data));
EXPECT_FALSE(is_dirty);
EXPECT_EQ(nullptr, data.get());
{
// Add an entry, see that it's reflected in the inspector interface.
auto writer = data_store_->GetWriterForOrigin(kTestOrigin,
TabVisibility::kBackground);
EXPECT_EQ(1U, inspector->GetAllInMemoryOrigins().size());
EXPECT_TRUE(inspector->GetDataForOrigin(kTestOrigin, &is_dirty, &data));
EXPECT_FALSE(is_dirty);
ASSERT_NE(nullptr, data.get());
// Touch the underlying data, see that the dirty bit updates.
writer->NotifySiteLoaded();
EXPECT_TRUE(inspector->GetDataForOrigin(kTestOrigin, &is_dirty, &data));
EXPECT_TRUE(is_dirty);
}
// Make sure the interface is unregistered from the profile on destruction.
data_store_.reset();
EXPECT_EQ(nullptr, LocalSiteCharacteristicsDataStoreInspector::GetForProfile(
&profile_));
}
} // namespace resource_coordinator