// Copyright (c) 2012 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/favicon/core/favicon_backend.h"

#include <memory>
#include <vector>

#include "base/containers/mru_cache.h"
#include "base/files/scoped_temp_dir.h"
#include "base/test/task_environment.h"
#include "components/favicon/core/favicon_backend_delegate.h"
#include "components/favicon/core/favicon_database.h"
#include "components/favicon/core/favicon_types.h"
#include "components/favicon_base/favicon_types.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"
#include "url/gurl.h"

namespace favicon {

namespace {

using favicon::FaviconBitmap;
using favicon::FaviconBitmapType;
using favicon::IconMapping;
using favicon_base::IconType;
using favicon_base::IconTypeSet;
using ::testing::ElementsAre;
using ::testing::UnorderedElementsAre;
using RedirectCache = base::MRUCache<GURL, std::vector<GURL>>;

const int kTinyEdgeSize = 10;
const int kSmallEdgeSize = 16;
const int kLargeEdgeSize = 32;

const gfx::Size kTinySize = gfx::Size(kTinyEdgeSize, kTinyEdgeSize);
const gfx::Size kSmallSize = gfx::Size(kSmallEdgeSize, kSmallEdgeSize);
const gfx::Size kLargeSize = gfx::Size(kLargeEdgeSize, kLargeEdgeSize);

}  // namespace

class FaviconBackendTest : public testing::Test, public FaviconBackendDelegate {
 public:
  FaviconBackendTest() = default;
  ~FaviconBackendTest() override = default;

  // testing::Test
  void SetUp() override {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    backend_ = FaviconBackend::Create(
        temp_dir_.GetPath().AppendASCII("favicons"), this);
    ASSERT_TRUE(backend_);
  }

  void TearDown() override { backend_.reset(); }

  // FaviconBackendDelegate:
  std::vector<GURL> GetCachedRecentRedirectsForPage(
      const GURL& page_url) override {
    auto iter = recent_redirects_.Get(page_url);
    if (iter != recent_redirects_.end())
      return iter->second;
    return {page_url};
  }

 protected:
  void SetFavicons(const base::flat_set<GURL>& page_urls,
                   favicon_base::IconType icon_type,
                   const GURL& icon_url,
                   const std::vector<SkBitmap>& bitmaps) {
    backend_->SetFavicons(page_urls, icon_type, icon_url, bitmaps,
                          FaviconBitmapType::ON_VISIT);
  }

  // Returns a vector with the small and large edge sizes.
  const std::vector<int> GetEdgeSizesSmallAndLarge() {
    std::vector<int> sizes_small_and_large;
    sizes_small_and_large.push_back(kSmallEdgeSize);
    sizes_small_and_large.push_back(kLargeEdgeSize);
    return sizes_small_and_large;
  }

  // Returns the number of icon mappings of |icon_type| to |page_url|.
  size_t NumIconMappingsForPageURL(const GURL& page_url, IconType icon_type) {
    std::vector<IconMapping> icon_mappings;
    backend_->db()->GetIconMappingsForPageURL(page_url, {icon_type},
                                              &icon_mappings);
    return icon_mappings.size();
  }

  // Returns the icon mappings for |page_url|.
  std::vector<IconMapping> GetIconMappingsForPageURL(const GURL& page_url) {
    std::vector<IconMapping> icon_mappings;
    backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings);
    return icon_mappings;
  }

  // Returns the favicon bitmaps for |icon_id| sorted by pixel size in
  // ascending order. Returns true if there is at least one favicon bitmap.
  bool GetSortedFaviconBitmaps(favicon_base::FaviconID icon_id,
                               std::vector<FaviconBitmap>* favicon_bitmaps) {
    if (!backend_->db()->GetFaviconBitmaps(icon_id, favicon_bitmaps))
      return false;
    std::sort(favicon_bitmaps->begin(), favicon_bitmaps->end(),
              [](const FaviconBitmap& a, const FaviconBitmap& b) {
                return a.pixel_size.GetArea() < b.pixel_size.GetArea();
              });
    return true;
  }

  // Returns true if there is exactly one favicon bitmap associated to
  // |favicon_id|. If true, returns favicon bitmap in output parameter.
  bool GetOnlyFaviconBitmap(const favicon_base::FaviconID icon_id,
                            FaviconBitmap* favicon_bitmap) {
    std::vector<FaviconBitmap> favicon_bitmaps;
    if (!backend_->db()->GetFaviconBitmaps(icon_id, &favicon_bitmaps))
      return false;
    if (favicon_bitmaps.size() != 1)
      return false;
    *favicon_bitmap = favicon_bitmaps[0];
    return true;
  }

  // Creates an |edge_size|x|edge_size| bitmap of |color|.
  SkBitmap CreateBitmap(SkColor color, int edge_size) {
    SkBitmap bitmap;
    bitmap.allocN32Pixels(edge_size, edge_size);
    bitmap.eraseColor(color);
    return bitmap;
  }

  // Returns true if |bitmap_data| is equal to |expected_data|.
  bool BitmapDataEqual(char expected_data,
                       scoped_refptr<base::RefCountedMemory> bitmap_data) {
    return bitmap_data.get() &&
           bitmap_data->size() == 1u &&
           *bitmap_data->front() == expected_data;
  }

  // Returns true if |bitmap_data| is of |color|.
  bool BitmapColorEqual(SkColor expected_color,
                        scoped_refptr<base::RefCountedMemory> bitmap_data) {
    SkBitmap bitmap;
    if (!gfx::PNGCodec::Decode(bitmap_data->front(), bitmap_data->size(),
                               &bitmap))
      return false;
    return expected_color == bitmap.getColor(0, 0);
  }

  base::test::TaskEnvironment task_environment_;
  std::unique_ptr<FaviconBackend> backend_;
  // Used in GetCachedRecentRedirectsForPage().
  RedirectCache recent_redirects_{8};

 private:
  base::ScopedTempDir temp_dir_;
};

// Test that SetFaviconMappingsForPageAndRedirects correctly updates icon
// mappings based on redirects, icon URLs and icon types.
TEST_F(FaviconBackendTest, SetFaviconMappingsForPageAndRedirects) {
  // Init recent_redirects_
  const GURL url1("http://www.google.com");
  const GURL url2("http://www.google.com/m");
  recent_redirects_.Put(url1, std::vector<GURL>{url2, url1});

  const GURL icon_url1("http://www.google.com/icon");
  const GURL icon_url2("http://www.google.com/icon2");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
  bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));

  // Add a favicon.
  SetFavicons({url1}, IconType::kFavicon, icon_url1, bitmaps);
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, IconType::kFavicon));
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, IconType::kFavicon));

  // Add one touch_icon
  SetFavicons({url1}, IconType::kTouchIcon, icon_url1, bitmaps);
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, IconType::kTouchIcon));
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, IconType::kTouchIcon));
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, IconType::kFavicon));

  // Add one kTouchPrecomposedIcon
  SetFavicons({url1}, IconType::kTouchPrecomposedIcon, icon_url1, bitmaps);
  // The touch_icon was replaced.
  EXPECT_EQ(0u, NumIconMappingsForPageURL(url1, IconType::kTouchIcon));
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, IconType::kFavicon));
  EXPECT_EQ(1u,
            NumIconMappingsForPageURL(url1, IconType::kTouchPrecomposedIcon));
  EXPECT_EQ(1u,
            NumIconMappingsForPageURL(url2, IconType::kTouchPrecomposedIcon));

  // Add a touch_icon.
  SetFavicons({url1}, IconType::kTouchIcon, icon_url1, bitmaps);
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, IconType::kTouchIcon));
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, IconType::kFavicon));
  // The kTouchPrecomposedIcon was replaced.
  EXPECT_EQ(0u,
            NumIconMappingsForPageURL(url1, IconType::kTouchPrecomposedIcon));

  // Add a web manifest_icon.
  SetFavicons({url1}, IconType::kWebManifestIcon, icon_url2, bitmaps);
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, IconType::kWebManifestIcon));
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, IconType::kFavicon));
  // The kTouchIcon was replaced.
  EXPECT_EQ(0u, NumIconMappingsForPageURL(url1, IconType::kTouchIcon));

  // The kTouchPrecomposedIcon was replaced.
  EXPECT_EQ(0u,
            NumIconMappingsForPageURL(url1, IconType::kTouchPrecomposedIcon));

  // Add a different favicon.
  SetFavicons({url1}, IconType::kFavicon, icon_url2, bitmaps);
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, IconType::kWebManifestIcon));
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, IconType::kFavicon));
  EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, IconType::kFavicon));
}

TEST_F(FaviconBackendTest,
       SetFaviconMappingsForPageAndRedirectsWithFragmentWithoutStripping) {
  const GURL url("http://www.google.com#abc");
  const GURL url_without_ref("http://www.google.com");
  const GURL icon_url("http://www.google.com/icon");
  SetFavicons(
      {url}, IconType::kFavicon, icon_url,
      std::vector<SkBitmap>{CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)});

  EXPECT_EQ(1u, NumIconMappingsForPageURL(url, IconType::kFavicon));
  EXPECT_EQ(0u, NumIconMappingsForPageURL(url_without_ref, IconType::kFavicon));
}

// Test that there is no churn in icon mappings from calling
// SetFavicons() twice with the same |bitmaps| parameter.
TEST_F(FaviconBackendTest, SetFaviconMappingsForPageDuplicates) {
  const GURL url("http://www.google.com/");
  const GURL icon_url("http://www.google.com/icon");

  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
  bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));

  SetFavicons({url}, IconType::kFavicon, icon_url, bitmaps);

  std::vector<IconMapping> icon_mappings;
  EXPECT_TRUE(backend_->db()->GetIconMappingsForPageURL(
      url, {IconType::kFavicon}, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  favicon::IconMappingID mapping_id = icon_mappings[0].mapping_id;

  SetFavicons({url}, IconType::kFavicon, icon_url, bitmaps);

  icon_mappings.clear();
  EXPECT_TRUE(backend_->db()->GetIconMappingsForPageURL(
      url, {IconType::kFavicon}, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());

  // The same row in the icon_mapping table should be used for the mapping as
  // before.
  EXPECT_EQ(mapping_id, icon_mappings[0].mapping_id);
}

// Test that calling SetFavicons() with FaviconBitmapData of different pixel
// sizes than the initially passed in FaviconBitmapData deletes the no longer
// used favicon bitmaps.
TEST_F(FaviconBackendTest, SetFaviconsDeleteBitmaps) {
  const GURL page_url("http://www.google.com/");
  const GURL icon_url("http://www.google.com/icon");

  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
  bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
  SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);

  // Test initial state.
  std::vector<IconMapping> icon_mappings = GetIconMappingsForPageURL(page_url);
  ASSERT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
  EXPECT_EQ(IconType::kFavicon, icon_mappings[0].icon_type);
  favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;

  std::vector<FaviconBitmap> favicon_bitmaps;
  EXPECT_TRUE(GetSortedFaviconBitmaps(favicon_id, &favicon_bitmaps));
  EXPECT_EQ(2u, favicon_bitmaps.size());
  favicon::FaviconBitmapID small_bitmap_id = favicon_bitmaps[0].bitmap_id;
  EXPECT_NE(0, small_bitmap_id);
  EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmaps[0].bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmaps[0].pixel_size);
  favicon::FaviconBitmapID large_bitmap_id = favicon_bitmaps[1].bitmap_id;
  EXPECT_NE(0, large_bitmap_id);
  EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, favicon_bitmaps[1].bitmap_data));
  EXPECT_EQ(kLargeSize, favicon_bitmaps[1].pixel_size);

  // Call SetFavicons() with bitmap data for only the large bitmap. Check that
  // the small bitmap is in fact deleted.
  bitmaps.clear();
  bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kLargeEdgeSize));
  SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);

  scoped_refptr<base::RefCountedMemory> bitmap_data_out;
  gfx::Size pixel_size_out;
  EXPECT_FALSE(backend_->db()->GetFaviconBitmap(
      small_bitmap_id, nullptr, nullptr, &bitmap_data_out, &pixel_size_out));
  EXPECT_TRUE(backend_->db()->GetFaviconBitmap(
      large_bitmap_id, nullptr, nullptr, &bitmap_data_out, &pixel_size_out));
  EXPECT_TRUE(BitmapColorEqual(SK_ColorWHITE, bitmap_data_out));
  EXPECT_EQ(kLargeSize, pixel_size_out);

  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
}

// Test updating a single favicon bitmap's data via SetFavicons.
TEST_F(FaviconBackendTest, SetFaviconsReplaceBitmapData) {
  const GURL page_url("http://www.google.com/");
  const GURL icon_url("http://www.google.com/icon");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));

  // Add bitmap to the database.
  SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);

  favicon_base::FaviconID original_favicon_id =
      backend_->db()->GetFaviconIDForFaviconURL(icon_url, IconType::kFavicon);
  EXPECT_NE(0, original_favicon_id);
  FaviconBitmap original_favicon_bitmap;
  EXPECT_TRUE(
      GetOnlyFaviconBitmap(original_favicon_id, &original_favicon_bitmap));
  EXPECT_TRUE(
      BitmapColorEqual(SK_ColorBLUE, original_favicon_bitmap.bitmap_data));
  EXPECT_NE(base::Time(), original_favicon_bitmap.last_updated);

  // Call SetFavicons() with completely identical data.
  bitmaps[0] = CreateBitmap(SK_ColorBLUE, kSmallEdgeSize);
  SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);

  favicon_base::FaviconID updated_favicon_id =
      backend_->db()->GetFaviconIDForFaviconURL(icon_url, IconType::kFavicon);
  EXPECT_NE(0, updated_favicon_id);
  FaviconBitmap updated_favicon_bitmap;
  EXPECT_TRUE(
      GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
  EXPECT_TRUE(
      BitmapColorEqual(SK_ColorBLUE, updated_favicon_bitmap.bitmap_data));
  EXPECT_NE(base::Time(), updated_favicon_bitmap.last_updated);

  // Call SetFavicons() with a different bitmap of the same size.
  bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize);
  SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);

  updated_favicon_id =
      backend_->db()->GetFaviconIDForFaviconURL(icon_url, IconType::kFavicon);
  EXPECT_NE(0, updated_favicon_id);
  EXPECT_TRUE(
      GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
  EXPECT_TRUE(
      BitmapColorEqual(SK_ColorWHITE, updated_favicon_bitmap.bitmap_data));

  // There should be no churn in FaviconIDs or FaviconBitmapIds even though
  // the bitmap data changed.
  EXPECT_EQ(original_favicon_bitmap.icon_id, updated_favicon_bitmap.icon_id);
  EXPECT_EQ(original_favicon_bitmap.bitmap_id,
            updated_favicon_bitmap.bitmap_id);
}

// Test that if two pages share the same FaviconID, changing the favicon for
// one page does not affect the other.
TEST_F(FaviconBackendTest, SetFaviconsSameFaviconURLForTwoPages) {
  GURL icon_url("http://www.google.com/favicon.ico");
  GURL icon_url_new("http://www.google.com/favicon2.ico");
  GURL page_url1("http://www.google.com");
  GURL page_url2("http://www.google.ca");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
  bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));

  SetFavicons({page_url1}, IconType::kFavicon, icon_url, bitmaps);

  backend_->UpdateFaviconMappingsAndFetch(
      {page_url2}, icon_url, IconType::kFavicon, GetEdgeSizesSmallAndLarge());

  // Check that the same FaviconID is mapped to both page URLs.
  std::vector<IconMapping> icon_mappings;
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url1, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
  EXPECT_NE(0, favicon_id);

  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url2, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);

  // Change the icon URL that |page_url1| is mapped to.
  bitmaps.clear();
  bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kSmallEdgeSize));
  SetFavicons({page_url1}, IconType::kFavicon, icon_url_new, bitmaps);

  // |page_url1| should map to a new FaviconID and have valid bitmap data.
  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url1, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(icon_url_new, icon_mappings[0].icon_url);
  EXPECT_NE(favicon_id, icon_mappings[0].icon_id);

  std::vector<FaviconBitmap> favicon_bitmaps;
  EXPECT_TRUE(backend_->db()->GetFaviconBitmaps(icon_mappings[0].icon_id,
                                                &favicon_bitmaps));
  EXPECT_EQ(1u, favicon_bitmaps.size());

  // |page_url2| should still map to the same FaviconID and have valid bitmap
  // data.
  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url2, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);

  favicon_bitmaps.clear();
  EXPECT_TRUE(backend_->db()->GetFaviconBitmaps(favicon_id, &favicon_bitmaps));
  EXPECT_EQ(2u, favicon_bitmaps.size());
}

// Test that if two pages share the same favicon, reported via a single call to
// SetFavicons(), it gets associated to both page URLs.
TEST_F(FaviconBackendTest, SetFaviconsWithTwoPageURLs) {
  GURL icon_url("http://www.google.com/favicon.ico");
  GURL page_url1("http://www.google.com");
  GURL page_url2("http://www.google.ca");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
  bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));

  SetFavicons({page_url1, page_url2}, IconType::kFavicon, icon_url, bitmaps);

  std::vector<IconMapping> icon_mappings;
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url1, &icon_mappings));
  ASSERT_EQ(1u, icon_mappings.size());
  favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
  EXPECT_NE(0, favicon_id);

  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url2, &icon_mappings));
  ASSERT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
}

// Test that favicon mappings can be deleted using DeleteFaviconMappings().
TEST_F(FaviconBackendTest, DeleteFaviconMappings) {
  GURL icon_url1("http://www.google.com/favicon.ico");
  GURL icon_url2("http://www.google.com/favicon2.ico");
  GURL page_url("http://www.google.com");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
  bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));

  // Setup
  SetFavicons({page_url}, IconType::kFavicon, icon_url1, bitmaps);
  SetFavicons({page_url}, IconType::kTouchIcon, icon_url2, bitmaps);

  // Delete one of the two mappings.
  backend_->DeleteFaviconMappings({page_url}, IconType::kTouchIcon);
  EXPECT_EQ(1u, NumIconMappingsForPageURL(page_url, IconType::kFavicon));
  EXPECT_EQ(0u, NumIconMappingsForPageURL(page_url, IconType::kTouchIcon));

  // Delete the second mapping.
  backend_->DeleteFaviconMappings({page_url}, IconType::kFavicon);
  EXPECT_EQ(0u, NumIconMappingsForPageURL(page_url, IconType::kFavicon));
}

// Tests calling SetOnDemandFavicons(). Neither |page_url| nor |icon_url| are
// known to the database.
TEST_F(FaviconBackendTest, SetOnDemandFaviconsForEmptyDB) {
  GURL page_url("http://www.google.com");
  GURL icon_url("http:/www.google.com/favicon.ico");

  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorRED, kSmallEdgeSize));

  EXPECT_TRUE(
      backend_
          ->SetOnDemandFavicons(page_url, IconType::kFavicon, icon_url, bitmaps)
          .did_update_bitmap);

  favicon_base::FaviconID favicon_id =
      backend_->db()->GetFaviconIDForFaviconURL(icon_url, IconType::kFavicon);
  EXPECT_NE(0, favicon_id);

  FaviconBitmap favicon_bitmap;
  ASSERT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
  // The newly set bitmap should have been retrieved.
  EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, favicon_bitmap.bitmap_data));
  // The favicon should be marked as expired.
  EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);

  // The raw bitmap result is marked as fetched on-demand.
  favicon_base::FaviconRawBitmapResult result =
      backend_->GetLargestFaviconForUrl(
          page_url, std::vector<IconTypeSet>({{IconType::kFavicon}}),
          kSmallEdgeSize);
  EXPECT_FALSE(result.fetched_because_of_page_visit);
}

// Tests calling SetOnDemandFavicons(). |page_url| is known to the database
// but |icon_url| is not (the second should be irrelevant though).
TEST_F(FaviconBackendTest, SetOnDemandFaviconsForPageInDB) {
  GURL page_url("http://www.google.com");
  GURL icon_url1("http:/www.google.com/favicon1.ico");
  GURL icon_url2("http:/www.google.com/favicon2.ico");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));

  // Add bitmap to the database.
  SetFavicons({page_url}, IconType::kFavicon, icon_url1, bitmaps);
  favicon_base::FaviconID original_favicon_id =
      backend_->db()->GetFaviconIDForFaviconURL(icon_url1, IconType::kFavicon);
  ASSERT_NE(0, original_favicon_id);

  // Call SetOnDemandFavicons() with a different icon URL and bitmap data.
  bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize);
  EXPECT_FALSE(backend_
                   ->SetOnDemandFavicons(page_url, IconType::kFavicon,
                                         icon_url2, bitmaps)
                   .did_update_bitmap);
  EXPECT_EQ(0, backend_->db()->GetFaviconIDForFaviconURL(icon_url2,
                                                         IconType::kFavicon));

  FaviconBitmap favicon_bitmap;
  ASSERT_TRUE(GetOnlyFaviconBitmap(original_favicon_id, &favicon_bitmap));
  // The original bitmap should have been retrieved.
  EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
  // The favicon should not be marked as expired.
  EXPECT_NE(base::Time(), favicon_bitmap.last_updated);

  // The raw bitmap result is not marked as fetched on-demand.
  favicon_base::FaviconRawBitmapResult result =
      backend_->GetLargestFaviconForUrl(
          page_url, std::vector<IconTypeSet>({{IconType::kFavicon}}),
          kSmallEdgeSize);
  EXPECT_TRUE(result.fetched_because_of_page_visit);
}

// Tests calling SetOnDemandFavicons(). |page_url| is not known to the
// database but |icon_url| is.
TEST_F(FaviconBackendTest, SetOnDemandFaviconsForIconInDB) {
  const GURL old_page_url("http://www.google.com/old");
  const GURL page_url("http://www.google.com/");
  const GURL icon_url("http://www.google.com/icon");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));

  // Add bitmap to the database.
  SetFavicons({old_page_url}, IconType::kFavicon, icon_url, bitmaps);
  favicon_base::FaviconID original_favicon_id =
      backend_->db()->GetFaviconIDForFaviconURL(icon_url, IconType::kFavicon);
  ASSERT_NE(0, original_favicon_id);

  // Call SetOnDemandFavicons() with a different bitmap.
  bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize);
  EXPECT_FALSE(
      backend_
          ->SetOnDemandFavicons(page_url, IconType::kFavicon, icon_url, bitmaps)
          .did_update_bitmap);

  EXPECT_EQ(original_favicon_id, backend_->db()->GetFaviconIDForFaviconURL(
                                     icon_url, IconType::kFavicon));

  FaviconBitmap favicon_bitmap;
  ASSERT_TRUE(GetOnlyFaviconBitmap(original_favicon_id, &favicon_bitmap));
  // The original bitmap should have been retrieved.
  EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
  // The favicon should not be marked as expired.
  EXPECT_NE(base::Time(), favicon_bitmap.last_updated);

  // The raw bitmap result is not marked as fetched on-demand.
  favicon_base::FaviconRawBitmapResult result =
      backend_->GetLargestFaviconForUrl(
          page_url, std::vector<IconTypeSet>({{IconType::kFavicon}}),
          kSmallEdgeSize);
  EXPECT_TRUE(result.fetched_because_of_page_visit);
}

// Test repeatedly calling MergeFavicon(). |page_url| is initially not known
// to the database.
TEST_F(FaviconBackendTest, MergeFaviconPageURLNotInDB) {
  GURL page_url("http://www.google.com");
  GURL icon_url("http:/www.google.com/favicon.ico");

  std::vector<unsigned char> data;
  data.push_back('a');
  scoped_refptr<base::RefCountedBytes> bitmap_data(
      new base::RefCountedBytes(data));

  backend_->MergeFavicon(page_url, icon_url, IconType::kFavicon, bitmap_data,
                         kSmallSize);

  // |page_url| should now be mapped to |icon_url| and the favicon bitmap should
  // be expired.
  std::vector<IconMapping> icon_mappings;
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(icon_url, icon_mappings[0].icon_url);

  FaviconBitmap favicon_bitmap;
  EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
  EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
  EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);

  data[0] = 'b';
  bitmap_data = new base::RefCountedBytes(data);
  backend_->MergeFavicon(page_url, icon_url, IconType::kFavicon, bitmap_data,
                         kSmallSize);

  // |page_url| should still have a single favicon bitmap. The bitmap data
  // should be updated.
  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(icon_url, icon_mappings[0].icon_url);

  EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
  EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
  EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
}

// Test calling MergeFavicon() when |page_url| is known to the database.
TEST_F(FaviconBackendTest, MergeFaviconPageURLInDB) {
  GURL page_url("http://www.google.com");
  GURL icon_url1("http:/www.google.com/favicon.ico");
  GURL icon_url2("http://www.google.com/favicon2.ico");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));

  SetFavicons({page_url}, IconType::kFavicon, icon_url1, bitmaps);

  // Test initial state.
  std::vector<IconMapping> icon_mappings;
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);

  FaviconBitmap favicon_bitmap;
  EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
  EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
  EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);

  // 1) Merge identical favicon bitmap.
  std::vector<unsigned char> data;
  gfx::PNGCodec::EncodeBGRASkBitmap(bitmaps[0], false, &data);
  scoped_refptr<base::RefCountedBytes> bitmap_data(
      new base::RefCountedBytes(data));
  backend_->MergeFavicon(page_url, icon_url1, IconType::kFavicon, bitmap_data,
                         kSmallSize);

  // All the data should stay the same and no notifications should have been
  // sent.
  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);

  EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
  EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
  EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);

  // 2) Merge favicon bitmap of the same size.
  data.clear();
  data.push_back('b');
  bitmap_data = new base::RefCountedBytes(data);
  backend_->MergeFavicon(page_url, icon_url1, IconType::kFavicon, bitmap_data,
                         kSmallSize);

  // The small favicon bitmap at |icon_url1| should be overwritten.
  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);

  EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
  EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
  EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);

  // 3) Merge favicon for the same icon URL, but a pixel size for which there is
  // no favicon bitmap.
  data[0] = 'c';
  bitmap_data = new base::RefCountedBytes(data);
  backend_->MergeFavicon(page_url, icon_url1, IconType::kFavicon, bitmap_data,
                         kTinySize);

  // A new favicon bitmap should be created and the preexisting favicon bitmap
  // ('b') should be expired.
  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);

  std::vector<FaviconBitmap> favicon_bitmaps;
  EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
                                      &favicon_bitmaps));
  EXPECT_EQ(base::Time(), favicon_bitmaps[0].last_updated);
  EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
  EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
  EXPECT_EQ(base::Time(), favicon_bitmaps[1].last_updated);
  EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);

  // 4) Merge favicon for an icon URL different from the icon URLs already
  // mapped to page URL.
  data[0] = 'd';
  bitmap_data = new base::RefCountedBytes(data);
  backend_->MergeFavicon(page_url, icon_url2, IconType::kFavicon, bitmap_data,
                         kSmallSize);

  // The existing favicon bitmaps should be copied over to the newly created
  // favicon at |icon_url2|. |page_url| should solely be mapped to |icon_url2|.
  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(icon_url2, icon_mappings[0].icon_url);

  favicon_bitmaps.clear();
  EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
                                      &favicon_bitmaps));
  EXPECT_EQ(base::Time(), favicon_bitmaps[0].last_updated);
  EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
  EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
  // The favicon being merged should take precedence over the preexisting
  // favicon bitmaps.
  EXPECT_EQ(base::Time(), favicon_bitmaps[1].last_updated);
  EXPECT_TRUE(BitmapDataEqual('d', favicon_bitmaps[1].bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
}

// Test calling MergeFavicon() when |icon_url| is known to the database but not
// mapped to |page_url|.
TEST_F(FaviconBackendTest, MergeFaviconIconURLMappedToDifferentPageURL) {
  GURL page_url1("http://www.google.com");
  GURL page_url2("http://news.google.com");
  GURL page_url3("http://maps.google.com");
  GURL icon_url("http:/www.google.com/favicon.ico");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));

  SetFavicons({page_url1}, IconType::kFavicon, icon_url, bitmaps);

  // Test initial state.
  std::vector<IconMapping> icon_mappings;
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url1, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(icon_url, icon_mappings[0].icon_url);

  FaviconBitmap favicon_bitmap;
  EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
  EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
  EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);

  // 1) Merge in an identical favicon bitmap data but for a different page URL.
  std::vector<unsigned char> data;
  gfx::PNGCodec::EncodeBGRASkBitmap(bitmaps[0], false, &data);
  scoped_refptr<base::RefCountedBytes> bitmap_data(
      new base::RefCountedBytes(data));

  backend_->MergeFavicon(page_url2, icon_url, IconType::kFavicon, bitmap_data,
                         kSmallSize);

  favicon_base::FaviconID favicon_id =
      backend_->db()->GetFaviconIDForFaviconURL(icon_url, IconType::kFavicon);
  EXPECT_NE(0, favicon_id);

  EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
  EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
  EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);

  // 2) Merging a favicon bitmap with different bitmap data for the same icon
  // URL should overwrite the small favicon bitmap at |icon_url|.
  data.clear();
  data.push_back('b');
  bitmap_data = new base::RefCountedBytes(data);
  backend_->MergeFavicon(page_url3, icon_url, IconType::kFavicon, bitmap_data,
                         kSmallSize);

  favicon_id =
      backend_->db()->GetFaviconIDForFaviconURL(icon_url, IconType::kFavicon);
  EXPECT_NE(0, favicon_id);

  EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
  EXPECT_EQ(base::Time(), favicon_bitmap.last_updated);
  EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
  EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);

  // |icon_url| should be mapped to all three page URLs.
  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url1, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);

  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url2, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);

  icon_mappings.clear();
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url3, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
}

// Test that MergeFavicon() does not add more than
// |kMaxFaviconBitmapsPerIconURL| to a favicon.
TEST_F(FaviconBackendTest, MergeFaviconMaxFaviconBitmapsPerIconURL) {
  GURL page_url("http://www.google.com");
  std::string icon_url_string("http://www.google.com/favicon.ico");
  size_t replace_index = icon_url_string.size() - 1;

  std::vector<unsigned char> data;
  data.push_back('a');
  scoped_refptr<base::RefCountedMemory> bitmap_data =
      base::RefCountedBytes::TakeVector(&data);

  int pixel_size = 1;
  for (size_t i = 0; i < kMaxFaviconBitmapsPerIconURL + 1; ++i) {
    icon_url_string[replace_index] = '0' + i;
    GURL icon_url(icon_url_string);

    backend_->MergeFavicon(page_url, icon_url, IconType::kFavicon, bitmap_data,
                           gfx::Size(pixel_size, pixel_size));
    ++pixel_size;
  }

  // There should be a single favicon mapped to |page_url| with exactly
  // kMaxFaviconBitmapsPerIconURL favicon bitmaps.
  std::vector<IconMapping> icon_mappings;
  EXPECT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  EXPECT_EQ(1u, icon_mappings.size());
  std::vector<FaviconBitmap> favicon_bitmaps;
  EXPECT_TRUE(backend_->db()->GetFaviconBitmaps(icon_mappings[0].icon_id,
                                                &favicon_bitmaps));
  EXPECT_EQ(kMaxFaviconBitmapsPerIconURL, favicon_bitmaps.size());
}

// Tests that the favicon set by MergeFavicon() shows up in the result of
// GetFaviconsForURL().
TEST_F(FaviconBackendTest, MergeFaviconShowsUpInGetFaviconsForURLResult) {
  GURL page_url("http://www.google.com");
  GURL icon_url("http://www.google.com/favicon.ico");
  GURL merged_icon_url("http://wwww.google.com/favicon2.ico");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
  bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));

  // Set some preexisting favicons for |page_url|.
  SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);

  // Merge small favicon.
  std::vector<unsigned char> data;
  data.push_back('c');
  scoped_refptr<base::RefCountedBytes> bitmap_data(
      new base::RefCountedBytes(data));
  backend_->MergeFavicon(page_url, merged_icon_url, IconType::kFavicon,
                         bitmap_data, kSmallSize);

  // Request favicon bitmaps for both 1x and 2x to simulate request done by
  // BookmarkModel::GetFavicon().
  std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results =
      backend_->GetFaviconsForUrl(page_url, {IconType::kFavicon},
                                  GetEdgeSizesSmallAndLarge(), false);

  EXPECT_EQ(2u, bitmap_results.size());
  const favicon_base::FaviconRawBitmapResult& first_result = bitmap_results[0];
  const favicon_base::FaviconRawBitmapResult& result =
      (first_result.pixel_size == kSmallSize) ? first_result
                                              : bitmap_results[1];
  EXPECT_TRUE(BitmapDataEqual('c', result.bitmap_data));
}

// Tests that calling MergeFavicon() with identical favicon data does not affect
// the favicon bitmap's "last updated" time. This is important because sync
// calls MergeFavicon() for all of the favicons that it manages at startup.
TEST_F(FaviconBackendTest, MergeIdenticalFaviconDoesNotChangeLastUpdatedTime) {
  GURL page_url("http://www.google.com");
  GURL icon_url("http://www.google.com/favicon.ico");
  std::vector<unsigned char> data;
  data.push_back('a');

  scoped_refptr<base::RefCountedBytes> bitmap_data(
      new base::RefCountedBytes(data));
  backend_->MergeFavicon(page_url, icon_url, IconType::kFavicon, bitmap_data,
                         kSmallSize);

  // Find the ID of the add favicon bitmap.
  std::vector<IconMapping> icon_mappings;
  ASSERT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  ASSERT_EQ(1u, icon_mappings.size());
  std::vector<FaviconBitmap> favicon_bitmaps;
  ASSERT_TRUE(backend_->db()->GetFaviconBitmaps(icon_mappings[0].icon_id,
                                                &favicon_bitmaps));

  // Change the last updated time of the just added favicon bitmap.
  const base::Time kLastUpdateTime =
      base::Time::Now() - base::TimeDelta::FromDays(314);
  backend_->db()->SetFaviconBitmapLastUpdateTime(favicon_bitmaps[0].bitmap_id,
                                                 kLastUpdateTime);

  // Call MergeFavicon() with identical data.
  backend_->MergeFavicon(page_url, icon_url, IconType::kFavicon, bitmap_data,
                         kSmallSize);

  // Check that the "last updated" time did not change.
  icon_mappings.clear();
  ASSERT_TRUE(
      backend_->db()->GetIconMappingsForPageURL(page_url, &icon_mappings));
  ASSERT_EQ(1u, icon_mappings.size());
  favicon_bitmaps.clear();
  ASSERT_TRUE(backend_->db()->GetFaviconBitmaps(icon_mappings[0].icon_id,
                                                &favicon_bitmaps));
  EXPECT_EQ(kLastUpdateTime, favicon_bitmaps[0].last_updated);
}

// Tests GetFaviconsForURL with icon_types priority,
TEST_F(FaviconBackendTest, TestGetFaviconsForURLWithIconTypesPriority) {
  GURL page_url("http://www.google.com");
  GURL icon_url("http://www.google.com/favicon.ico");
  GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");

  std::vector<SkBitmap> favicon_bitmaps;
  favicon_bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
  favicon_bitmaps.push_back(CreateBitmap(SK_ColorRED, 32));

  std::vector<SkBitmap> touch_bitmaps;
  touch_bitmaps.push_back(CreateBitmap(SK_ColorWHITE, 64));

  // Set some preexisting favicons for |page_url|.
  SetFavicons({page_url}, IconType::kFavicon, icon_url, favicon_bitmaps);
  SetFavicons({page_url}, IconType::kTouchIcon, touch_icon_url, touch_bitmaps);

  std::vector<IconTypeSet> icon_types;
  icon_types.push_back({IconType::kFavicon});
  icon_types.push_back({IconType::kTouchIcon});

  favicon_base::FaviconRawBitmapResult result =
      backend_->GetLargestFaviconForUrl(page_url, icon_types, 16);

  // Verify the result icon is 32x32 favicon.
  EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
  EXPECT_EQ(IconType::kFavicon, result.icon_type);

  // Change Minimal size to 32x32 and verify the 64x64 touch icon returned.
  result = backend_->GetLargestFaviconForUrl(page_url, icon_types, 32);
  EXPECT_EQ(gfx::Size(64, 64), result.pixel_size);
  EXPECT_EQ(IconType::kTouchIcon, result.icon_type);
}

// Test the the first types of icon is returned if its size equal to the
// second types icon.
TEST_F(FaviconBackendTest, TestGetFaviconsForURLReturnFavicon) {
  GURL page_url("http://www.google.com");
  GURL icon_url("http://www.google.com/favicon.ico");
  GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");

  std::vector<SkBitmap> favicon_bitmaps;
  favicon_bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
  favicon_bitmaps.push_back(CreateBitmap(SK_ColorRED, 32));

  std::vector<SkBitmap> touch_bitmaps;
  touch_bitmaps.push_back(CreateBitmap(SK_ColorWHITE, 32));

  // Set some preexisting favicons for |page_url|.
  SetFavicons({page_url}, IconType::kFavicon, icon_url, favicon_bitmaps);
  SetFavicons({page_url}, IconType::kTouchIcon, touch_icon_url, touch_bitmaps);

  std::vector<IconTypeSet> icon_types;
  icon_types.push_back({IconType::kFavicon});
  icon_types.push_back({IconType::kTouchIcon});

  favicon_base::FaviconRawBitmapResult result =
      backend_->GetLargestFaviconForUrl(page_url, icon_types, 16);

  // Verify the result icon is 32x32 favicon.
  EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
  EXPECT_EQ(IconType::kFavicon, result.icon_type);

  // Change minimal size to 32x32 and verify the 32x32 favicon returned.
  favicon_base::FaviconRawBitmapResult result1 =
      backend_->GetLargestFaviconForUrl(page_url, icon_types, 32);
  EXPECT_EQ(gfx::Size(32, 32), result1.pixel_size);
  EXPECT_EQ(IconType::kFavicon, result1.icon_type);
}

// Test the favicon is returned if its size is smaller than minimal size,
// because it is only one available.
TEST_F(FaviconBackendTest, TestGetFaviconsForURLReturnFaviconEvenItSmaller) {
  GURL page_url("http://www.google.com");
  GURL icon_url("http://www.google.com/favicon.ico");

  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));

  // Set preexisting favicons for |page_url|.
  SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);

  std::vector<IconTypeSet> icon_types;
  icon_types.push_back({IconType::kFavicon});
  icon_types.push_back({IconType::kTouchIcon});

  favicon_base::FaviconRawBitmapResult result =
      backend_->GetLargestFaviconForUrl(page_url, icon_types, 32);

  // Verify 16x16 icon is returned, even it small than minimal_size.
  EXPECT_EQ(gfx::Size(16, 16), result.pixel_size);
  EXPECT_EQ(IconType::kFavicon, result.icon_type);
}

// Test the results of GetFaviconsForUrl() when there are no found favicons.
TEST_F(FaviconBackendTest, GetFaviconsForUrlEmpty) {
  const GURL page_url("http://www.google.com/");

  std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results =
      backend_->GetFaviconsForUrl(page_url, {IconType::kFavicon},
                                  GetEdgeSizesSmallAndLarge(), false);
  EXPECT_TRUE(bitmap_results.empty());
}

// Test the results of GetFaviconsForUrl() when there are matching favicons
// but there are no associated favicon bitmaps.
TEST_F(FaviconBackendTest, GetFaviconsForUrlNoFaviconBitmaps) {
  const GURL page_url("http://www.google.com/");
  const GURL icon_url("http://www.google.com/icon1");

  favicon_base::FaviconID icon_id =
      backend_->db()->AddFavicon(icon_url, IconType::kFavicon);
  EXPECT_NE(0, icon_id);
  EXPECT_NE(0, backend_->db()->AddIconMapping(page_url, icon_id));

  std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out =
      backend_->GetFaviconsForUrl(page_url, {IconType::kFavicon},
                                  GetEdgeSizesSmallAndLarge(), false);
  EXPECT_TRUE(bitmap_results_out.empty());
}

// Test that GetFaviconsForUrl() returns results for the bitmaps which most
// closely match the passed in the desired pixel sizes.
TEST_F(FaviconBackendTest, GetFaviconsForUrlSelectClosestMatch) {
  const GURL page_url("http://www.google.com/");
  const GURL icon_url("http://www.google.com/icon1");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kTinyEdgeSize));
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
  bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));

  SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);

  std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out =
      backend_->GetFaviconsForUrl(page_url, {IconType::kFavicon},
                                  GetEdgeSizesSmallAndLarge(), false);

  // The bitmap data for the small and large bitmaps should be returned as their
  // sizes match exactly.
  EXPECT_EQ(2u, bitmap_results_out.size());
  // No required order for results.
  if (bitmap_results_out[0].pixel_size == kLargeSize) {
    favicon_base::FaviconRawBitmapResult tmp_result = bitmap_results_out[0];
    bitmap_results_out[0] = bitmap_results_out[1];
    bitmap_results_out[1] = tmp_result;
  }

  EXPECT_FALSE(bitmap_results_out[0].expired);
  EXPECT_TRUE(
      BitmapColorEqual(SK_ColorBLUE, bitmap_results_out[0].bitmap_data));
  EXPECT_EQ(kSmallSize, bitmap_results_out[0].pixel_size);
  EXPECT_EQ(icon_url, bitmap_results_out[0].icon_url);
  EXPECT_EQ(IconType::kFavicon, bitmap_results_out[0].icon_type);

  EXPECT_FALSE(bitmap_results_out[1].expired);
  EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, bitmap_results_out[1].bitmap_data));
  EXPECT_EQ(kLargeSize, bitmap_results_out[1].pixel_size);
  EXPECT_EQ(icon_url, bitmap_results_out[1].icon_url);
  EXPECT_EQ(IconType::kFavicon, bitmap_results_out[1].icon_type);
}

// Test the results of GetFaviconsForUrl() when called with different
// |icon_types|.
TEST_F(FaviconBackendTest, GetFaviconsForUrlIconType) {
  const GURL page_url("http://www.google.com/");
  const GURL icon_url1("http://www.google.com/icon1.png");
  const GURL icon_url2("http://www.google.com/icon2.png");
  std::vector<SkBitmap> bitmaps;
  bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));

  std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
  SetFavicons({page_url}, IconType::kFavicon, icon_url1, bitmaps);
  SetFavicons({page_url}, IconType::kTouchIcon, icon_url2, bitmaps);

  std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out =
      backend_->GetFaviconsForUrl(page_url, {IconType::kFavicon},
                                  GetEdgeSizesSmallAndLarge(), false);

  EXPECT_EQ(1u, bitmap_results_out.size());
  EXPECT_EQ(IconType::kFavicon, bitmap_results_out[0].icon_type);
  EXPECT_EQ(icon_url1, bitmap_results_out[0].icon_url);

  bitmap_results_out = backend_->GetFaviconsForUrl(
      page_url, {IconType::kTouchIcon}, GetEdgeSizesSmallAndLarge(), false);

  EXPECT_EQ(1u, bitmap_results_out.size());
  EXPECT_EQ(IconType::kTouchIcon, bitmap_results_out[0].icon_type);
  EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url);
}

// Test that GetFaviconsForUrl() behaves correctly for different values of
// |fallback_to_host|.
TEST_F(FaviconBackendTest, GetFaviconsForUrlFallbackToHost) {
  const GURL page_url_http("http://www.google.com/");
  const GURL page_url_https("https://www.google.com/");
  const GURL page_url_http_same_prefix("http://www.google.com.au/");
  const GURL page_url_http_same_suffix("http://m.www.google.com/");
  const GURL page_url_different_scheme("file://www.google.com/");
  const GURL icon_url1("http://www.google.com.au/icon.png");
  const GURL icon_url2("http://maps.google.com.au/icon.png");
  const GURL icon_url3("https://www.google.com/icon.png");

  std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
  SetFavicons({page_url_http_same_prefix}, IconType::kFavicon, icon_url1,
              {CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)});
  SetFavicons({page_url_http_same_suffix}, IconType::kFavicon, icon_url2,
              {CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)});

  {
    // Querying for the http URL with |fallback_to_host|=false returns nothing.
    std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out =
        backend_->GetFaviconsForUrl(page_url_http,
                                    {IconType::kFavicon, IconType::kTouchIcon},
                                    {kSmallEdgeSize}, false);

    EXPECT_TRUE(bitmap_results_out.empty());

    // Querying for the http URL with |fallback_to_host|=true should not return
    // the favicon associated with a different host, even when that host has the
    // same prefix or suffix.
    bitmap_results_out = backend_->GetFaviconsForUrl(
        page_url_http, {IconType::kFavicon, IconType::kTouchIcon},
        {kSmallEdgeSize}, true);

    EXPECT_TRUE(bitmap_results_out.empty());
  }

  SetFavicons({page_url_https}, IconType::kFavicon, icon_url3,
              {CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)});

  {
    // Querying for the http URL with |fallback_to_host|=false returns nothing.
    std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out =
        backend_->GetFaviconsForUrl(page_url_http,
                                    {IconType::kFavicon, IconType::kTouchIcon},
                                    {kSmallEdgeSize}, false);

    EXPECT_TRUE(bitmap_results_out.empty());

    // Querying for the http URL with |fallback_to_host|=true returns the
    // favicon associated with the https URL.
    bitmap_results_out = backend_->GetFaviconsForUrl(
        page_url_http, {IconType::kFavicon, IconType::kTouchIcon},
        {kSmallEdgeSize}, true);

    ASSERT_EQ(1u, bitmap_results_out.size());
    EXPECT_EQ(icon_url3, bitmap_results_out[0].icon_url);
  }

  {
    // Querying for a URL with non HTTP/HTTPS scheme returns nothing even if
    // |fallback_to_host| is true.
    std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out =
        backend_->GetFaviconsForUrl(page_url_different_scheme,
                                    {IconType::kFavicon, IconType::kTouchIcon},
                                    {kSmallEdgeSize}, false);

    EXPECT_TRUE(bitmap_results_out.empty());

    bitmap_results_out = backend_->GetFaviconsForUrl(
        page_url_different_scheme, {IconType::kFavicon, IconType::kTouchIcon},
        {kSmallEdgeSize}, true);

    EXPECT_TRUE(bitmap_results_out.empty());
  }
}

// Test that when GetFaviconsForUrl() is called with multiple icon types that
// the best favicon bitmap is selected from among all of the icon types.
TEST_F(FaviconBackendTest, GetFaviconsForUrlMultipleIconTypes) {
  const GURL page_url("http://www.google.com/");
  const GURL icon_url1("http://www.google.com/icon1.png");
  const GURL icon_url2("http://www.google.com/icon2.png");

  std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
  SetFavicons({page_url}, IconType::kFavicon, icon_url1,
              {CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)});
  SetFavicons({page_url}, IconType::kTouchIcon, icon_url2,
              {CreateBitmap(SK_ColorBLUE, kLargeEdgeSize)});

  struct TestCase {
    int desired_edge_size;
    GURL expected_icon_url;
  } kTestCases[]{{kSmallEdgeSize, icon_url1}, {kLargeEdgeSize, icon_url2}};

  for (const TestCase& test_case : kTestCases) {
    std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results =
        backend_->GetFaviconsForUrl(page_url,
                                    {IconType::kFavicon, IconType::kTouchIcon},
                                    {test_case.desired_edge_size}, false);

    ASSERT_EQ(1u, bitmap_results.size());
    EXPECT_EQ(test_case.expected_icon_url, bitmap_results[0].icon_url);
  }
}

// Test that GetFaviconsForUrl() correctly sets the expired flag for bitmap
// reults.
TEST_F(FaviconBackendTest, GetFaviconsForUrlExpired) {
  const GURL page_url("http://www.google.com/");
  const GURL icon_url("http://www.google.com/icon.png");

  std::vector<unsigned char> data;
  data.push_back('a');
  scoped_refptr<base::RefCountedBytes> bitmap_data(
      base::RefCountedBytes::TakeVector(&data));
  base::Time last_updated = base::Time::FromTimeT(0);
  favicon_base::FaviconID icon_id = backend_->db()->AddFavicon(
      icon_url, IconType::kFavicon, bitmap_data, FaviconBitmapType::ON_VISIT,
      last_updated, kSmallSize);
  EXPECT_NE(0, icon_id);
  EXPECT_NE(0, backend_->db()->AddIconMapping(page_url, icon_id));

  std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out =
      backend_->GetFaviconsForUrl(page_url, {IconType::kFavicon},
                                  GetEdgeSizesSmallAndLarge(), false);

  EXPECT_EQ(1u, bitmap_results_out.size());
  EXPECT_TRUE(bitmap_results_out[0].expired);
}

}  // namespace favicon
