910e62b5创建于 1月15日历史提交
// 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 "chrome/browser/content_index/content_index_provider_impl.h"

#include <memory>

#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/time/time.h"
#include "chrome/browser/engagement/site_engagement_service_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/test/test_history_database.h"
#include "components/offline_items_collection/core/offline_content_provider.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_index_provider.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_storage_partition.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace {

using offline_items_collection::ContentId;
using offline_items_collection::OfflineContentAggregator;
using offline_items_collection::OfflineContentProvider;
using offline_items_collection::OfflineItem;
using offline_items_collection::OfflineItemVisuals;
using offline_items_collection::UpdateDelta;
using testing::_;

constexpr int64_t kServiceWorkerRegistrationId = 42;
constexpr double kEngagementScore = 42.0;
constexpr char kLaunchUrl[] = "https://example.com/foo";

// Hosts the test profile. Global to be accessible from
// |BuildTestHistoryService|.
base::FilePath profile_path;

std::unique_ptr<KeyedService> BuildTestHistoryService(
    content::BrowserContext* context) {
  auto service = std::make_unique<history::HistoryService>();
  service->Init(history::TestHistoryDatabaseParamsForPath(profile_path));
  return std::move(service);
}

std::unique_ptr<KeyedService> BuildTestSiteEngagementService(
    content::BrowserContext* context) {
  auto service = std::make_unique<site_engagement::SiteEngagementService>(
      static_cast<Profile*>(context));
  service->ResetBaseScoreForURL(GURL(kLaunchUrl).DeprecatedGetOriginAsURL(),
                                kEngagementScore);
  return std::move(service);
}

}  // namespace

class ContentIndexProviderImplTest : public testing::Test,
                                     public OfflineContentProvider::Observer {
 public:
  void SetUp() override {
    TestingProfile::Builder builder;
    builder.AddTestingFactory(HistoryServiceFactory::GetInstance(),
                              base::BindRepeating(&BuildTestHistoryService));
    builder.AddTestingFactory(
        site_engagement::SiteEngagementServiceFactory::GetInstance(),
        base::BindRepeating(&BuildTestSiteEngagementService));

    ASSERT_TRUE(profile_dir_.CreateUniqueTempDir());
    profile_path = profile_dir_.GetPath();
    builder.SetPath(profile_dir_.GetPath());

    profile_ = builder.Build();

    provider_ = std::make_unique<ContentIndexProviderImpl>(profile_.get());
    provider_->AddObserver(this);
  }

  void TearDown() override { provider_->RemoveObserver(this); }

  // OfflineContentProvider::Observer implementation.
  MOCK_METHOD1(OnItemsAdded,
               void(const OfflineContentProvider::OfflineItemList& items));
  MOCK_METHOD1(OnItemRemoved, void(const ContentId& id));
  void OnItemUpdated(const OfflineItem& item,
                     const std::optional<UpdateDelta>& update_delta) override {
    NOTREACHED();
  }
  void OnContentProviderGoingDown() override {}

  content::ContentIndexEntry CreateEntry(const std::string& id,
                                         bool is_top_level_context = true) {
    auto description = blink::mojom::ContentDescription::New(
        id, "title", "description", blink::mojom::ContentCategory::ARTICLE,
        std::vector<blink::mojom::ContentIconDefinitionPtr>(), "launch_url");
    return content::ContentIndexEntry(kServiceWorkerRegistrationId,
                                      std::move(description), GURL(kLaunchUrl),
                                      base::Time::Now(), is_top_level_context);
  }

 protected:
  base::ScopedTempDir profile_dir_;
  content::BrowserTaskEnvironment task_environment_;
  std::unique_ptr<TestingProfile> profile_;
  std::unique_ptr<ContentIndexProviderImpl> provider_;
};

TEST_F(ContentIndexProviderImplTest, OfflineItemCreation) {
  std::vector<OfflineItem> items;
  {
    EXPECT_CALL(*this, OnItemsAdded(_)).WillOnce(testing::SaveArg<0>(&items));
    provider_->OnContentAdded(CreateEntry("id"));
  }
  ASSERT_EQ(items.size(), 1u);
  const auto& item = items[0];

  EXPECT_FALSE(item.id.name_space.empty());
  EXPECT_FALSE(item.id.id.empty());
  EXPECT_FALSE(item.title.empty());
  EXPECT_FALSE(item.description.empty());
  EXPECT_FALSE(item.is_transient);
  EXPECT_TRUE(item.is_suggested);
  EXPECT_TRUE(item.is_openable);
  EXPECT_EQ(item.url, GURL(kLaunchUrl));
  EXPECT_EQ(item.content_quality_score, kEngagementScore / 100.0);
}

TEST_F(ContentIndexProviderImplTest, ObserverUpdates) {
  {
    EXPECT_CALL(*this, OnItemsAdded(_));
    provider_->OnContentAdded(CreateEntry("id"));
  }

  // Adding an already existing ID should call update.
  {
    EXPECT_CALL(*this, OnItemRemoved(_)).Times(1);
    EXPECT_CALL(*this, OnItemsAdded(_)).Times(1);
    provider_->OnContentAdded(CreateEntry("id"));
  }

  {
    EXPECT_CALL(*this, OnItemRemoved(_));
    provider_->OnContentDeleted(kServiceWorkerRegistrationId,
                                url::Origin::Create(GURL(kLaunchUrl)), "id");
  }

  {
    EXPECT_CALL(*this, OnItemsAdded(_)).Times(0);
    provider_->OnContentAdded(
        CreateEntry("id", /* is_top_level_context= */ false));
  }
}