blob: b7903e786a8a37d8428bece9675904722d6f1b10 [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"));
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 {
public:
LocalSiteCharacteristicsDataStoreTest()
: scoped_set_tick_clock_for_testing_(&test_clock_) {
scoped_feature_list_.InitAndEnableFeature(
features::kSiteCharacteristicsDatabase);
data_store_ =
std::make_unique<LocalSiteCharacteristicsDataStore>(&profile_);
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();
}
protected:
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_;
std::unique_ptr<LocalSiteCharacteristicsDataStore> data_store_;
};
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());
data_store_->OnURLsDeleted(nullptr, history::DeletionInfo::ForAllHistory());
}
TEST_F(LocalSiteCharacteristicsDataStoreTest, HistoryServiceObserver) {
// Mock the database to ensure that the delete events get propagated properly.
::testing::StrictMock<MockLocalSiteCharacteristicsDatabase>* mock_db =
new ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase>();
data_store_->SetDatabaseForTesting(base::WrapUnique(mock_db));
// Load a first origin, and then make use of a feature on it.
auto reader = data_store_->GetReaderForOrigin(kTestOrigin);
EXPECT_TRUE(reader);
auto writer =
data_store_->GetWriterForOrigin(kTestOrigin, TabVisibility::kBackground);
EXPECT_TRUE(writer);
internal::LocalSiteCharacteristicsDataImpl* data =
data_store_->origin_data_map_for_testing().find(kTestOrigin)->second;
EXPECT_TRUE(data);
constexpr base::TimeDelta kDelay = base::TimeDelta::FromHours(1);
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown,
reader->UpdatesTitleInBackground());
writer->NotifySiteLoaded();
base::TimeDelta last_loaded_time = data->last_loaded_time_for_testing();
writer->NotifyUpdatesTitleInBackground();
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse,
reader->UpdatesTitleInBackground());
test_clock_.Advance(kDelay);
// Load a second origin, make use of a feature on it too.
auto reader2 = data_store_->GetReaderForOrigin(kTestOrigin2);
EXPECT_TRUE(reader2);
auto writer2 =
data_store_->GetWriterForOrigin(kTestOrigin2, TabVisibility::kBackground);
EXPECT_TRUE(writer2);
writer2->NotifySiteLoaded();
writer2->NotifyUpdatesFaviconInBackground();
// This site hasn'be been unloaded yet, so the last loaded time shouldn't have
// changed.
EXPECT_EQ(data->last_loaded_time_for_testing(), last_loaded_time);
// 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())};
EXPECT_CALL(*mock_db,
RemoveSiteCharacteristicsFromDB(::testing::ContainerEq(
std::vector<url::Origin>({kTestOrigin, kOriginNotInMap}))));
data_store_->OnURLsDeleted(nullptr, history::DeletionInfo::ForUrls(
urls_to_delete, std::set<GURL>()));
::testing::Mock::VerifyAndClear(mock_db);
// The information for this site have been reset, so the last loaded time
// should now be equal to the current time and the title update feature
// observation should have been cleared.
EXPECT_NE(data->last_loaded_time_for_testing(), last_loaded_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(SiteFeatureUsage::kSiteFeatureInUse,
reader2->UpdatesFaviconInBackground());
test_clock_.Advance(kDelay);
// 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);
EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown,
reader2->UpdatesFaviconInBackground());
EXPECT_EQ(data->last_loaded_time_for_testing(),
test_clock_.NowTicks() - base::TimeTicks::UnixEpoch());
internal::LocalSiteCharacteristicsDataImpl* data2 =
data_store_->origin_data_map_for_testing().find(kTestOrigin2)->second;
EXPECT_TRUE(data2);
EXPECT_EQ(data2->last_loaded_time_for_testing(),
test_clock_.NowTicks() - base::TimeTicks::UnixEpoch());
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