// Copyright 2013 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/sync_sessions/favicon_cache.h" #include #include #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "sync/api/attachments/attachment_id.h" #include "sync/api/sync_change_processor_wrapper_for_test.h" #include "sync/api/sync_error_factory_mock.h" #include "sync/api/time.h" #include "sync/internal_api/public/attachments/attachment_service_proxy_for_test.h" #include "sync/protocol/favicon_image_specifics.pb.h" #include "sync/protocol/favicon_tracking_specifics.pb.h" #include "sync/protocol/sync.pb.h" #include "testing/gtest/include/gtest/gtest.h" namespace browser_sync { namespace { // Total number of favicons to use in sync test batches. const int kFaviconBatchSize = 10; // Maximum number of favicons to sync. const int kMaxSyncFavicons = kFaviconBatchSize*2; // TestChangeProcessor -------------------------------------------------------- // Dummy SyncChangeProcessor used to help review what SyncChanges are pushed // back up to Sync. class TestChangeProcessor : public syncer::SyncChangeProcessor { public: TestChangeProcessor(); ~TestChangeProcessor() override; // Store a copy of all the changes passed in so we can examine them later. syncer::SyncError ProcessSyncChanges( const tracked_objects::Location& from_here, const syncer::SyncChangeList& change_list) override; syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const override { return syncer::SyncDataList(); } bool contains_guid(const std::string& guid) const { return change_map_.count(guid) != 0; } syncer::SyncChange change_for_guid(const std::string& guid) const { DCHECK(contains_guid(guid)); return change_map_.find(guid)->second; } // Returns the last change list received, and resets the internal list. syncer::SyncChangeList GetAndResetChangeList() { syncer::SyncChangeList list; list.swap(change_list_); return list; } void set_erroneous(bool erroneous) { erroneous_ = erroneous; } private: // Track the changes received in ProcessSyncChanges. std::map change_map_; syncer::SyncChangeList change_list_; bool erroneous_; DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor); }; TestChangeProcessor::TestChangeProcessor() : erroneous_(false) { } TestChangeProcessor::~TestChangeProcessor() { } syncer::SyncError TestChangeProcessor::ProcessSyncChanges( const tracked_objects::Location& from_here, const syncer::SyncChangeList& change_list) { if (erroneous_) { return syncer::SyncError( FROM_HERE, syncer::SyncError::DATATYPE_ERROR, "Some error.", change_list[0].sync_data().GetDataType()); } change_list_.insert(change_list_.end(), change_list.begin(), change_list.end()); change_map_.erase(change_map_.begin(), change_map_.end()); for (syncer::SyncChangeList::const_iterator iter = change_list.begin(); iter != change_list.end(); ++iter) { change_map_[iter->sync_data().GetTitle()] = *iter; } return syncer::SyncError(); } // TestFaviconData ------------------------------------------------------------ struct TestFaviconData { TestFaviconData() : last_visit_time(0), is_bookmarked(false) {} GURL page_url; GURL icon_url; std::string image_16; std::string image_32; std::string image_64; int64_t last_visit_time; bool is_bookmarked; }; TestFaviconData BuildFaviconData(int index) { TestFaviconData data; data.page_url = GURL(base::StringPrintf("http://bla.com/%.2i.html", index)); data.icon_url = GURL(base::StringPrintf("http://bla.com/%.2i.ico", index)); data.image_16 = base::StringPrintf("16 %i", index); // TODO(zea): enable this once the cache supports writing them. // data.image_32 = base::StringPrintf("32 %i", index); // data.image_64 = base::StringPrintf("64 %i", index); data.last_visit_time = index; return data; } void FillImageSpecifics( const TestFaviconData& test_data, sync_pb::FaviconImageSpecifics* image_specifics) { image_specifics->set_favicon_url(test_data.icon_url.spec()); if (!test_data.image_16.empty()) { image_specifics->mutable_favicon_web()->set_height(16); image_specifics->mutable_favicon_web()->set_width(16); image_specifics->mutable_favicon_web()->set_favicon(test_data.image_16); } if (!test_data.image_32.empty()) { image_specifics->mutable_favicon_web_32()->set_height(32); image_specifics->mutable_favicon_web_32()->set_width(32); image_specifics->mutable_favicon_web_32()->set_favicon(test_data.image_32); } if (!test_data.image_64.empty()) { image_specifics->mutable_favicon_touch_64()->set_height(64); image_specifics->mutable_favicon_touch_64()->set_width(64); image_specifics->mutable_favicon_touch_64()-> set_favicon(test_data.image_64); } } void FillTrackingSpecifics( const TestFaviconData& test_data, sync_pb::FaviconTrackingSpecifics* tracking_specifics) { tracking_specifics->set_favicon_url(test_data.icon_url.spec()); tracking_specifics->set_last_visit_time_ms(test_data.last_visit_time); tracking_specifics->set_is_bookmarked(test_data.is_bookmarked); } testing::AssertionResult CompareFaviconDataToSpecifics( const TestFaviconData& test_data, const sync_pb::EntitySpecifics& specifics) { if (specifics.has_favicon_image()) { sync_pb::FaviconImageSpecifics image_specifics = specifics.favicon_image(); if (image_specifics.favicon_url() != test_data.icon_url.spec()) return testing::AssertionFailure() << "Image icon url doesn't match."; if (!test_data.image_16.empty()) { if (image_specifics.favicon_web().favicon() != test_data.image_16 || image_specifics.favicon_web().height() != 16 || image_specifics.favicon_web().width() != 16) { return testing::AssertionFailure() << "16p image data doesn't match."; } } else if (image_specifics.has_favicon_web()) { return testing::AssertionFailure() << "Missing 16p favicon."; } if (!test_data.image_32.empty()) { if (image_specifics.favicon_web_32().favicon() != test_data.image_32 || image_specifics.favicon_web().height() != 32 || image_specifics.favicon_web().width() != 32) { return testing::AssertionFailure() << "32p image data doesn't match."; } } else if (image_specifics.has_favicon_web_32()) { return testing::AssertionFailure() << "Missing 32p favicon."; } if (!test_data.image_64.empty()) { if (image_specifics.favicon_touch_64().favicon() != test_data.image_64 || image_specifics.favicon_web().height() != 64 || image_specifics.favicon_web().width() != 64) { return testing::AssertionFailure() << "64p image data doesn't match."; } } else if (image_specifics.has_favicon_touch_64()) { return testing::AssertionFailure() << "Missing 64p favicon."; } } else { sync_pb::FaviconTrackingSpecifics tracking_specifics = specifics.favicon_tracking(); if (tracking_specifics.favicon_url() != test_data.icon_url.spec()) return testing::AssertionFailure() << "Tracking icon url doesn't match."; if (tracking_specifics.last_visit_time_ms() != test_data.last_visit_time) return testing::AssertionFailure() << "Visit time doesn't match."; if (tracking_specifics.is_bookmarked() != test_data.is_bookmarked) return testing::AssertionFailure() << "Bookmark status doens't match."; } return testing::AssertionSuccess(); } testing::AssertionResult VerifyChanges( syncer::ModelType expected_model_type, const std::vector& expected_change_types, const std::vector& expected_icons, const syncer::SyncChangeList& change_list) { DCHECK_EQ(expected_change_types.size(), expected_icons.size()); if (change_list.size() != expected_icons.size()) return testing::AssertionFailure() << "Change list size doesn't match."; for (size_t i = 0; i < expected_icons.size(); ++i) { TestFaviconData data = BuildFaviconData(expected_icons[i]); if (change_list[i].sync_data().GetDataType() != expected_model_type) return testing::AssertionFailure() << "Change datatype doesn't match."; if (change_list[i].change_type() != expected_change_types[i]) return testing::AssertionFailure() << "Change type doesn't match."; if (change_list[i].change_type() == syncer::SyncChange::ACTION_DELETE) { if (syncer::SyncDataLocal(change_list[i].sync_data()).GetTag() != data.icon_url.spec()) return testing::AssertionFailure() << "Deletion url does not match."; } else { testing::AssertionResult compare_result = CompareFaviconDataToSpecifics( data, change_list[i].sync_data().GetSpecifics()); if (!compare_result) return compare_result; } } return testing::AssertionSuccess(); } // Helper to extract the favicon id embedded in the tag of a sync // change. int GetFaviconId(const syncer::SyncChange change) { std::string tag = syncer::SyncDataLocal(change.sync_data()).GetTag(); const std::string kPrefix = "http://bla.com/"; const std::string kSuffix = ".ico"; if (tag.find(kPrefix) != 0) return -1; std::string temp = tag.substr(kPrefix.length()); if (temp.rfind(kSuffix) <= 0) return -1; temp = temp.substr(0, temp.rfind(kSuffix)); int result = -1; if (!base::StringToInt(temp, &result)) return -1; return result; } } // namespace class SyncFaviconCacheTest : public testing::Test { public: SyncFaviconCacheTest(); ~SyncFaviconCacheTest() override {} void SetUpInitialSync(const syncer::SyncDataList& initial_image_data, const syncer::SyncDataList& initial_tracking_data); size_t GetFaviconCount() const; size_t GetTaskCount() const; testing::AssertionResult ExpectFaviconEquals( const std::string& page_url, const std::string& bytes) const; testing::AssertionResult VerifyLocalIcons( const std::vector& expected_icons); testing::AssertionResult VerifyLocalCustomIcons( const std::vector& expected_icons); scoped_ptr CreateAndPassProcessor(); scoped_ptr CreateAndPassSyncErrorFactory(); FaviconCache* cache() { return &cache_; } TestChangeProcessor* processor() { return sync_processor_.get(); } // Finish an outstanding favicon load for the icon described in |test_data|. void OnCustomFaviconDataAvailable(const TestFaviconData& test_data); // Helper method to run the message loop after invoking // OnReceivedSyncFavicon, which posts an internal task. void TriggerSyncFaviconReceived(const GURL& page_url, const GURL& icon_url, const std::string& icon_bytes, int64_t last_visit_time_ms); private: base::MessageLoopForUI message_loop_; FaviconCache cache_; // Our dummy ChangeProcessor used to inspect changes pushed to Sync. scoped_ptr sync_processor_; scoped_ptr sync_processor_wrapper_; }; SyncFaviconCacheTest::SyncFaviconCacheTest() : cache_(nullptr, nullptr, kMaxSyncFavicons), sync_processor_(new TestChangeProcessor), sync_processor_wrapper_(new syncer::SyncChangeProcessorWrapperForTest( sync_processor_.get())) {} void SyncFaviconCacheTest::SetUpInitialSync( const syncer::SyncDataList& initial_image_data, const syncer::SyncDataList& initial_tracking_data) { cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES, initial_image_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); ASSERT_EQ(0U, processor()->GetAndResetChangeList().size()); cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING, initial_tracking_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); ASSERT_EQ(0U, processor()->GetAndResetChangeList().size()); } size_t SyncFaviconCacheTest::GetFaviconCount() const { return cache_.NumFaviconsForTest(); } size_t SyncFaviconCacheTest::GetTaskCount() const { return cache_.NumTasksForTest(); } testing::AssertionResult SyncFaviconCacheTest::ExpectFaviconEquals( const std::string& page_url, const std::string& bytes) const { GURL gurl(page_url); scoped_refptr favicon; if (!cache_.GetSyncedFaviconForPageURL(gurl, &favicon)) return testing::AssertionFailure() << "Favicon is missing."; if (favicon->size() != bytes.size()) return testing::AssertionFailure() << "Favicon sizes don't match."; for (size_t i = 0; i < favicon->size(); ++i) { if (bytes[i] != *(favicon->front() + i)) return testing::AssertionFailure() << "Favicon data doesn't match."; } return testing::AssertionSuccess(); } testing::AssertionResult SyncFaviconCacheTest::VerifyLocalIcons( const std::vector& expected_icons) { std::vector expected_custom_icons; for (size_t i = 0; i < expected_icons.size(); ++i) { expected_custom_icons.push_back(BuildFaviconData(expected_icons[i])); } return VerifyLocalCustomIcons(expected_custom_icons); } testing::AssertionResult SyncFaviconCacheTest::VerifyLocalCustomIcons( const std::vector& expected_custom_icons) { syncer::SyncDataList image_data_list = cache()->GetAllSyncData(syncer::FAVICON_IMAGES); syncer::SyncDataList tracking_data_list = cache()->GetAllSyncData(syncer::FAVICON_TRACKING); if (expected_custom_icons.size() > image_data_list.size() || expected_custom_icons.size() > tracking_data_list.size()) return testing::AssertionFailure() << "Number of icons doesn't match."; for (size_t i = 0; i < expected_custom_icons.size(); ++i) { const TestFaviconData& test_data = expected_custom_icons[i]; // Find the test data in the data lists. Assume that both lists have the // same ordering, which may not match the |expected_custom_icons| ordering. bool found_match = false; for (size_t j = 0; j < image_data_list.size(); ++j) { if (image_data_list[j].GetTitle() != test_data.icon_url.spec()) continue; found_match = true; const sync_pb::FaviconImageSpecifics& image_specifics = image_data_list[j].GetSpecifics().favicon_image(); sync_pb::FaviconImageSpecifics expected_image_specifics; FillImageSpecifics(test_data, &expected_image_specifics); if (image_specifics.SerializeAsString() != expected_image_specifics.SerializeAsString()) { return testing::AssertionFailure() << "Image data doesn't match."; } const sync_pb::FaviconTrackingSpecifics& tracking_specifics = tracking_data_list[j].GetSpecifics().favicon_tracking(); sync_pb::FaviconTrackingSpecifics expected_tracking_specifics; FillTrackingSpecifics(test_data, &expected_tracking_specifics); if (tracking_specifics.SerializeAsString() != expected_tracking_specifics.SerializeAsString()) { return testing::AssertionFailure() << "Tracking data doesn't match."; } } if (!found_match) return testing::AssertionFailure() << "Could not find favicon."; } return testing::AssertionSuccess(); } scoped_ptr SyncFaviconCacheTest::CreateAndPassProcessor() { return scoped_ptr( new syncer::SyncChangeProcessorWrapperForTest(sync_processor_.get())); } scoped_ptr SyncFaviconCacheTest:: CreateAndPassSyncErrorFactory() { return scoped_ptr( new syncer::SyncErrorFactoryMock()); } void SyncFaviconCacheTest::OnCustomFaviconDataAvailable( const TestFaviconData& test_data) { std::vector bitmap_results; if (!test_data.image_16.empty()) { favicon_base::FaviconRawBitmapResult bitmap_result; bitmap_result.icon_url = test_data.icon_url; bitmap_result.pixel_size.set_width(16); bitmap_result.pixel_size.set_height(16); base::RefCountedString* temp_string = new base::RefCountedString(); temp_string->data() = test_data.image_16; bitmap_result.bitmap_data = temp_string; bitmap_results.push_back(bitmap_result); } if (!test_data.image_32.empty()) { favicon_base::FaviconRawBitmapResult bitmap_result; bitmap_result.icon_url = test_data.icon_url; bitmap_result.pixel_size.set_width(32); bitmap_result.pixel_size.set_height(32); base::RefCountedString* temp_string = new base::RefCountedString(); temp_string->data() = test_data.image_32; bitmap_result.bitmap_data = temp_string; bitmap_results.push_back(bitmap_result); } if (!test_data.image_64.empty()) { favicon_base::FaviconRawBitmapResult bitmap_result; bitmap_result.icon_url = test_data.icon_url; bitmap_result.pixel_size.set_width(64); bitmap_result.pixel_size.set_height(64); base::RefCountedString* temp_string = new base::RefCountedString(); temp_string->data() = test_data.image_64; bitmap_result.bitmap_data = temp_string; bitmap_results.push_back(bitmap_result); } cache()->OnFaviconDataAvailable(test_data.page_url, bitmap_results); } void SyncFaviconCacheTest::TriggerSyncFaviconReceived( const GURL& page_url, const GURL& icon_url, const std::string& icon_bytes, int64_t last_visit_time_ms) { cache()->OnReceivedSyncFavicon(page_url, icon_url, icon_bytes, last_visit_time_ms); message_loop_.RunUntilIdle(); } // A freshly constructed cache should be empty. TEST_F(SyncFaviconCacheTest, Empty) { EXPECT_EQ(0U, GetFaviconCount()); } TEST_F(SyncFaviconCacheTest, ReceiveSyncFavicon) { std::string page_url = "http://www.google.com"; std::string fav_url = "http://www.google.com/favicon.ico"; std::string bytes = "bytes"; EXPECT_EQ(0U, GetFaviconCount()); TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), bytes, 0); EXPECT_EQ(1U, GetFaviconCount()); EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes)); } TEST_F(SyncFaviconCacheTest, ReceiveEmptySyncFavicon) { std::string page_url = "http://www.google.com"; std::string fav_url = "http://www.google.com/favicon.ico"; std::string bytes = "bytes"; EXPECT_EQ(0U, GetFaviconCount()); TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), std::string(), 0); EXPECT_EQ(0U, GetFaviconCount()); EXPECT_FALSE(ExpectFaviconEquals(page_url, std::string())); // Then receive the actual favicon. TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), bytes, 0); EXPECT_EQ(1U, GetFaviconCount()); EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes)); } TEST_F(SyncFaviconCacheTest, ReceiveUpdatedSyncFavicon) { std::string page_url = "http://www.google.com"; std::string fav_url = "http://www.google.com/favicon.ico"; std::string bytes = "bytes"; std::string bytes2 = "bytes2"; EXPECT_EQ(0U, GetFaviconCount()); TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), bytes, 0); EXPECT_EQ(1U, GetFaviconCount()); EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes)); // The cache should not update existing favicons from tab sync favicons // (which can be reassociated several times). TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), bytes2, 0); EXPECT_EQ(1U, GetFaviconCount()); EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes)); EXPECT_FALSE(ExpectFaviconEquals(page_url, bytes2)); } TEST_F(SyncFaviconCacheTest, MultipleMappings) { std::string page_url = "http://www.google.com"; std::string page2_url = "http://bla.google.com"; std::string fav_url = "http://www.google.com/favicon.ico"; std::string bytes = "bytes"; EXPECT_EQ(0U, GetFaviconCount()); TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), bytes, 0); EXPECT_EQ(1U, GetFaviconCount()); EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes)); // Map another page to the same favicon. They should share the same data. TriggerSyncFaviconReceived(GURL(page2_url), GURL(fav_url), bytes, 0); EXPECT_EQ(1U, GetFaviconCount()); EXPECT_TRUE(ExpectFaviconEquals(page2_url, bytes)); } TEST_F(SyncFaviconCacheTest, SyncEmpty) { syncer::SyncMergeResult merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES, syncer::SyncDataList(), CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(0U, cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size()); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(0, merge_result.num_items_added()); EXPECT_EQ(0, merge_result.num_items_modified()); EXPECT_EQ(0, merge_result.num_items_deleted()); EXPECT_EQ(0, merge_result.num_items_before_association()); EXPECT_EQ(0, merge_result.num_items_after_association()); merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING, syncer::SyncDataList(), CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(0U, cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size()); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(0, merge_result.num_items_added()); EXPECT_EQ(0, merge_result.num_items_modified()); EXPECT_EQ(0, merge_result.num_items_deleted()); EXPECT_EQ(0, merge_result.num_items_before_association()); EXPECT_EQ(0, merge_result.num_items_after_association()); } // Setting up sync with existing local favicons should push those favicons into // sync. TEST_F(SyncFaviconCacheTest, SyncExistingLocal) { std::vector expected_change_types; std::vector expected_icons; for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData favicon = BuildFaviconData(i); TriggerSyncFaviconReceived(favicon.page_url, favicon.icon_url, favicon.image_16, i); expected_change_types.push_back(syncer::SyncChange::ACTION_ADD); expected_icons.push_back(i); } syncer::SyncMergeResult merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES, syncer::SyncDataList(), CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(static_cast(kFaviconBatchSize), cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size()); syncer::SyncChangeList change_list = processor()->GetAndResetChangeList(); EXPECT_TRUE(VerifyChanges(syncer::FAVICON_IMAGES, expected_change_types, expected_icons, change_list)); EXPECT_EQ(0, merge_result.num_items_added()); EXPECT_EQ(0, merge_result.num_items_modified()); EXPECT_EQ(0, merge_result.num_items_deleted()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association()); merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING, syncer::SyncDataList(), CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(static_cast(kFaviconBatchSize), cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size()); change_list = processor()->GetAndResetChangeList(); EXPECT_TRUE(VerifyChanges(syncer::FAVICON_TRACKING, expected_change_types, expected_icons, change_list)); EXPECT_EQ(0, merge_result.num_items_added()); EXPECT_EQ(0, merge_result.num_items_modified()); EXPECT_EQ(0, merge_result.num_items_deleted()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association()); } // Setting up sync with existing sync data should load that data into the local // cache. TEST_F(SyncFaviconCacheTest, SyncExistingRemote) { syncer::SyncDataList initial_image_data, initial_tracking_data; std::vector expected_icons; for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } syncer::SyncMergeResult merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES, initial_image_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(static_cast(kFaviconBatchSize), cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size()); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_added()); EXPECT_EQ(0, merge_result.num_items_modified()); EXPECT_EQ(0, merge_result.num_items_deleted()); EXPECT_EQ(0, merge_result.num_items_before_association()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association()); merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING, initial_tracking_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(static_cast(kFaviconBatchSize), cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size()); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(0, merge_result.num_items_added()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_modified()); EXPECT_EQ(0, merge_result.num_items_deleted()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association()); ASSERT_TRUE(VerifyLocalIcons(expected_icons)); } // Setting up sync with local data and sync data should merge the two image // sets, with remote data having priority in case both exist. TEST_F(SyncFaviconCacheTest, SyncMergesImages) { // First go through and add local 16p favicons. for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData favicon = BuildFaviconData(i); TriggerSyncFaviconReceived(favicon.page_url, favicon.icon_url, favicon.image_16, i); } // Then go through and create the initial sync data, which does not have 16p // favicons for the first half, and has custom 16p favicons for the second. std::vector expected_change_types; std::vector expected_icons; std::vector expected_data; syncer::SyncDataList initial_image_data, initial_tracking_data; for (int i = 0; i < kFaviconBatchSize; ++i) { sync_pb::EntitySpecifics image_specifics, tracking_specifics; TestFaviconData test_data = BuildFaviconData(i); if (i < kFaviconBatchSize/2) { test_data.image_16 = std::string(); expected_icons.push_back(i); expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE); } else { test_data.image_16 += "custom"; expected_data.push_back(test_data); } FillImageSpecifics(test_data, image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(test_data, tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } syncer::SyncMergeResult merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES, initial_image_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(static_cast(kFaviconBatchSize), cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size()); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); EXPECT_EQ(static_cast(kFaviconBatchSize)/2, changes.size()); EXPECT_EQ(0, merge_result.num_items_added()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_modified()); EXPECT_EQ(0, merge_result.num_items_deleted()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association()); merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING, initial_tracking_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(static_cast(kFaviconBatchSize), cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size()); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(0, merge_result.num_items_added()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_modified()); EXPECT_EQ(0, merge_result.num_items_deleted()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association()); ASSERT_TRUE(VerifyLocalIcons(expected_icons)); ASSERT_TRUE(VerifyLocalCustomIcons(expected_data)); ASSERT_TRUE(VerifyChanges(syncer::FAVICON_IMAGES, expected_change_types, expected_icons, changes)); } // Setting up sync with local data and sync data should merge the two tracking // sets, such that the visit time is the most recent. TEST_F(SyncFaviconCacheTest, SyncMergesTracking) { // First go through and add local 16p favicons. for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData favicon = BuildFaviconData(i); TriggerSyncFaviconReceived(favicon.page_url, favicon.icon_url, favicon.image_16, i); } // Then go through and create the initial sync data, which for the first half // the local has a newer visit, and for the second the remote does. std::vector expected_change_types; std::vector expected_icons; std::vector expected_data; syncer::SyncDataList initial_image_data, initial_tracking_data; for (int i = 0; i < kFaviconBatchSize; ++i) { sync_pb::EntitySpecifics image_specifics, tracking_specifics; TestFaviconData test_data = BuildFaviconData(i); if (i < kFaviconBatchSize/2) { test_data.last_visit_time = i-1; expected_icons.push_back(i); expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE); } else { test_data.last_visit_time = i+1; expected_data.push_back(test_data); } FillImageSpecifics(test_data, image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(test_data, tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } syncer::SyncMergeResult merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES, initial_image_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(static_cast(kFaviconBatchSize), cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size()); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(0, merge_result.num_items_added()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_modified()); EXPECT_EQ(0, merge_result.num_items_deleted()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association()); merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING, initial_tracking_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(static_cast(kFaviconBatchSize), cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size()); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); EXPECT_EQ(static_cast(kFaviconBatchSize)/2, changes.size()); EXPECT_EQ(0, merge_result.num_items_added()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_modified()); EXPECT_EQ(0, merge_result.num_items_deleted()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association()); EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association()); ASSERT_TRUE(VerifyLocalIcons(expected_icons)); ASSERT_TRUE(VerifyLocalCustomIcons(expected_data)); ASSERT_TRUE(VerifyChanges(syncer::FAVICON_TRACKING, expected_change_types, expected_icons, changes)); } // Receiving old icons (missing image data) should result in pushing the new // merged icons back to the remote syncer. TEST_F(SyncFaviconCacheTest, ReceiveStaleImages) { syncer::SyncDataList initial_image_data, initial_tracking_data; syncer::SyncChangeList stale_changes; std::vector expected_icons; std::vector expected_change_types; for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE); image_specifics.mutable_favicon_image()->clear_favicon_web(); stale_changes.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_UPDATE, syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } SetUpInitialSync(initial_image_data, initial_tracking_data); // Now receive the same icons as an update, but with missing image data. cache()->ProcessSyncChanges(FROM_HERE, stale_changes); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); ASSERT_TRUE(VerifyLocalIcons(expected_icons)); ASSERT_EQ(static_cast(kFaviconBatchSize), changes.size()); ASSERT_TRUE(VerifyChanges(syncer::FAVICON_IMAGES, expected_change_types, expected_icons, changes)); } // New icons should be added locally without pushing anything back to the // remote syncer. TEST_F(SyncFaviconCacheTest, ReceiveNewImages) { syncer::SyncDataList initial_image_data, initial_tracking_data; syncer::SyncChangeList new_changes; std::vector expected_icons; for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); TestFaviconData test_data = BuildFaviconData(i); sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(test_data, image_specifics.mutable_favicon_image()); new_changes.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_UPDATE, syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); image_specifics.mutable_favicon_image() ->mutable_favicon_web() ->mutable_favicon() ->append("old"); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } SetUpInitialSync(initial_image_data, initial_tracking_data); // Now receive the new icons as an update. cache()->ProcessSyncChanges(FROM_HERE, new_changes); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); ASSERT_TRUE(VerifyLocalIcons(expected_icons)); } // Recieving the same icons as the local data should have no effect. TEST_F(SyncFaviconCacheTest, ReceiveSameImages) { syncer::SyncDataList initial_image_data, initial_tracking_data; syncer::SyncChangeList same_changes; std::vector expected_icons; for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); TestFaviconData test_data = BuildFaviconData(i); sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(test_data, image_specifics.mutable_favicon_image()); same_changes.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_UPDATE, syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } SetUpInitialSync(initial_image_data, initial_tracking_data); // Now receive the new icons as an update. cache()->ProcessSyncChanges(FROM_HERE, same_changes); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); ASSERT_TRUE(VerifyLocalIcons(expected_icons)); } // Receiving stale tracking (old visit times) should result in pushing back // the newer visit times to the remote syncer. TEST_F(SyncFaviconCacheTest, ReceiveStaleTracking) { syncer::SyncDataList initial_image_data, initial_tracking_data; syncer::SyncChangeList stale_changes; std::vector expected_icons; std::vector expected_change_types; for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); tracking_specifics.mutable_favicon_tracking()->set_last_visit_time_ms(-1); stale_changes.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_UPDATE, syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); } SetUpInitialSync(initial_image_data, initial_tracking_data); // Now receive the same icons as an update, but with missing image data. cache()->ProcessSyncChanges(FROM_HERE, stale_changes); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); ASSERT_TRUE(VerifyLocalIcons(expected_icons)); ASSERT_EQ(static_cast(kFaviconBatchSize), changes.size()); ASSERT_TRUE(VerifyChanges(syncer::FAVICON_TRACKING, expected_change_types, expected_icons, changes)); } // New tracking information should be added locally without pushing anything // back to the remote syncer. TEST_F(SyncFaviconCacheTest, ReceiveNewTracking) { syncer::SyncDataList initial_image_data, initial_tracking_data; syncer::SyncChangeList new_changes; std::vector expected_icons; // We start from one here so that we don't have to deal with a -1 visit time. for (int i = 1; i <= kFaviconBatchSize; ++i) { expected_icons.push_back(i); sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); new_changes.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_UPDATE, syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); tracking_specifics.mutable_favicon_tracking()->set_last_visit_time_ms(i-1); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } SetUpInitialSync(initial_image_data, initial_tracking_data); // Now receive the new icons as an update. cache()->ProcessSyncChanges(FROM_HERE, new_changes); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); ASSERT_TRUE(VerifyLocalIcons(expected_icons)); } // Receiving the same tracking information as the local data should have no // effect. TEST_F(SyncFaviconCacheTest, ReceiveSameTracking) { syncer::SyncDataList initial_image_data, initial_tracking_data; syncer::SyncChangeList same_changes; std::vector expected_icons; for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); same_changes.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_UPDATE, syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); } SetUpInitialSync(initial_image_data, initial_tracking_data); // Now receive the new icons as an update. cache()->ProcessSyncChanges(FROM_HERE, same_changes); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); ASSERT_TRUE(VerifyLocalIcons(expected_icons)); } // Verify we can delete favicons after setting up sync. TEST_F(SyncFaviconCacheTest, DeleteFavicons) { syncer::SyncDataList initial_image_data, initial_tracking_data; syncer::SyncChangeList tracking_deletions, image_deletions; for (int i = 0; i < kFaviconBatchSize; ++i) { sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); tracking_deletions.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_DELETE, syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); image_deletions.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_DELETE, syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); } SetUpInitialSync(initial_image_data, initial_tracking_data); // Now receive the tracking deletions. Since we'll still have orphan data, // the favicon count should remain the same. EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); cache()->ProcessSyncChanges(FROM_HERE, tracking_deletions); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); // Once the image deletions arrive, the favicon count should be 0 again. cache()->ProcessSyncChanges(FROM_HERE, image_deletions); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(0U, GetFaviconCount()); } // Ensure that MergeDataAndStartSyncing enforces the sync favicon limit by // dropping local icons. TEST_F(SyncFaviconCacheTest, ExpireOnMergeData) { std::vector expected_icons; syncer::SyncDataList initial_image_data, initial_tracking_data; // Set up sync so it has the maximum number of favicons, while the local has // the same amount of different favicons. for (int i = 0; i < kMaxSyncFavicons; ++i) { sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); expected_icons.push_back(i); TestFaviconData favicon = BuildFaviconData(i+kMaxSyncFavicons); TriggerSyncFaviconReceived(favicon.page_url, favicon.icon_url, favicon.image_16, i+kMaxSyncFavicons); } EXPECT_FALSE(VerifyLocalIcons(expected_icons)); // Drops image part of the unsynced icons. syncer::SyncMergeResult merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES, initial_image_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(static_cast(kMaxSyncFavicons)*2, GetFaviconCount()); // Still have tracking. EXPECT_EQ(static_cast(kMaxSyncFavicons), cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size()); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_added()); EXPECT_EQ(0, merge_result.num_items_modified()); EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_deleted()); EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_before_association()); EXPECT_EQ(kMaxSyncFavicons * 2, merge_result.num_items_after_association()); // Drops tracking part of the unsynced icons. merge_result = cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING, initial_tracking_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); EXPECT_EQ(static_cast(kMaxSyncFavicons), cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size()); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(0, merge_result.num_items_added()); EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_modified()); EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_deleted()); EXPECT_EQ(kMaxSyncFavicons * 2, merge_result.num_items_before_association()); EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_after_association()); EXPECT_TRUE(VerifyLocalIcons(expected_icons)); } // Receiving sync additions (via ProcessSyncChanges) should not trigger // expirations. TEST_F(SyncFaviconCacheTest, NoExpireOnProcessSyncChanges) { syncer::SyncDataList initial_image_data, initial_tracking_data; syncer::SyncChangeList image_changes, tracking_changes; std::vector expected_icons; for (int i = 0; i < kMaxSyncFavicons; ++i) { expected_icons.push_back(i); sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); // Set up new tracking specifics for the icons received at change time. expected_icons.push_back(i + kMaxSyncFavicons); FillImageSpecifics(BuildFaviconData(i + kMaxSyncFavicons), image_specifics.mutable_favicon_image()); image_changes.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_ADD, syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); FillTrackingSpecifics(BuildFaviconData(i + kMaxSyncFavicons), tracking_specifics.mutable_favicon_tracking()); tracking_changes.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_ADD, syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); } SetUpInitialSync(initial_image_data, initial_tracking_data); // Now receive the new icons as an update. EXPECT_EQ(static_cast(kMaxSyncFavicons), GetFaviconCount()); cache()->ProcessSyncChanges(FROM_HERE, image_changes); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); cache()->ProcessSyncChanges(FROM_HERE, tracking_changes); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_TRUE(VerifyLocalIcons(expected_icons)); EXPECT_LT(static_cast(kMaxSyncFavicons), GetFaviconCount()); } // Test that visiting a new page triggers a favicon load and a sync addition. TEST_F(SyncFaviconCacheTest, AddOnFaviconVisited) { EXPECT_EQ(0U, GetFaviconCount()); SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList()); std::vector expected_icons; for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); TestFaviconData test_data = BuildFaviconData(i); cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url); } EXPECT_EQ(static_cast(kFaviconBatchSize), GetTaskCount()); for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData test_data = BuildFaviconData(i); OnCustomFaviconDataAvailable(test_data); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); ASSERT_EQ(2U, changes.size()); EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type()); EXPECT_EQ(syncer::FAVICON_IMAGES, changes[0].sync_data().GetDataType()); EXPECT_TRUE( CompareFaviconDataToSpecifics(test_data, changes[0].sync_data().GetSpecifics())); EXPECT_EQ(syncer::FAVICON_TRACKING, changes[1].sync_data().GetDataType()); // Just verify the favicon url for the tracking specifics and that the // timestamp is non-null. EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[1].change_type()); EXPECT_EQ(test_data.icon_url.spec(), changes[1].sync_data().GetSpecifics().favicon_tracking(). favicon_url()); EXPECT_NE(changes[1].sync_data().GetSpecifics().favicon_tracking(). last_visit_time_ms(), 0); } EXPECT_EQ(0U, GetTaskCount()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); } // Test that visiting a known page does not trigger a favicon load and just // updates the sync tracking info. TEST_F(SyncFaviconCacheTest, UpdateOnFaviconVisited) { EXPECT_EQ(0U, GetFaviconCount()); SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList()); std::vector expected_icons; // Add the favicons. for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); TestFaviconData test_data = BuildFaviconData(i); cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url); OnCustomFaviconDataAvailable(test_data); } syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); // Visit the favicons again. EXPECT_EQ(0U, GetTaskCount()); for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData test_data = BuildFaviconData(i); cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); ASSERT_EQ(1U, changes.size()); // Just verify the favicon url for the tracking specifics and that the // timestamp is non-null. EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type()); EXPECT_EQ(test_data.icon_url.spec(), changes[0].sync_data().GetSpecifics().favicon_tracking(). favicon_url()); EXPECT_NE(changes[0].sync_data().GetSpecifics().favicon_tracking(). last_visit_time_ms(), 0); } EXPECT_EQ(0U, GetTaskCount()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); } // Ensure we properly expire old synced favicons as new ones are updated. TEST_F(SyncFaviconCacheTest, ExpireOnFaviconVisited) { EXPECT_EQ(0U, GetFaviconCount()); SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList()); std::vector expected_icons; // Add the initial favicons. for (int i = 0; i < kMaxSyncFavicons; ++i) { expected_icons.push_back(i); TestFaviconData test_data = BuildFaviconData(i); cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url); OnCustomFaviconDataAvailable(test_data); } syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); // Visit some new favicons, triggering expirations of the old favicons. EXPECT_EQ(0U, GetTaskCount()); for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData old_favicon = BuildFaviconData(i); TestFaviconData test_data = BuildFaviconData(i + kMaxSyncFavicons); cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url); OnCustomFaviconDataAvailable(test_data); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); ASSERT_EQ(4U, changes.size()); EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type()); EXPECT_TRUE( CompareFaviconDataToSpecifics(test_data, changes[0].sync_data().GetSpecifics())); EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, changes[1].change_type()); EXPECT_EQ(old_favicon.icon_url.spec(), syncer::SyncDataLocal(changes[1].sync_data()).GetTag()); EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[2].change_type()); EXPECT_EQ(test_data.icon_url.spec(), changes[2].sync_data().GetSpecifics().favicon_tracking(). favicon_url()); EXPECT_NE(changes[2].sync_data().GetSpecifics().favicon_tracking(). last_visit_time_ms(), 0); EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, changes[3].change_type()); EXPECT_EQ(old_favicon.icon_url.spec(), syncer::SyncDataLocal(changes[3].sync_data()).GetTag()); } EXPECT_EQ(0U, GetTaskCount()); EXPECT_EQ(static_cast(kMaxSyncFavicons), GetFaviconCount()); } // A full history clear notification should result in all synced favicons being // deleted. TEST_F(SyncFaviconCacheTest, HistoryFullClear) { syncer::SyncDataList initial_image_data, initial_tracking_data; std::vector expected_icons; std::vector expected_deletions; for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); expected_deletions.push_back(syncer::SyncChange::ACTION_DELETE); TestFaviconData test_data = BuildFaviconData(i); sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(test_data, image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } SetUpInitialSync(initial_image_data, initial_tracking_data); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); EXPECT_TRUE(changes.empty()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); cache()->OnURLsDeleted(nullptr, true, false, history::URLRows(), std::set()); EXPECT_EQ(0U, GetFaviconCount()); changes = processor()->GetAndResetChangeList(); ASSERT_EQ(changes.size(), static_cast(kFaviconBatchSize)*2); syncer::SyncChangeList changes_1, changes_2; for (int i = 0; i < kFaviconBatchSize; ++i) { changes_1.push_back(changes[i]); changes_2.push_back(changes[i + kFaviconBatchSize]); } VerifyChanges(syncer::FAVICON_IMAGES, expected_deletions, expected_icons, changes_1); VerifyChanges(syncer::FAVICON_TRACKING, expected_deletions, expected_icons, changes_2); } // A partial history clear notification should result in the expired favicons // also being deleted from sync. TEST_F(SyncFaviconCacheTest, HistorySubsetClear) { syncer::SyncDataList initial_image_data, initial_tracking_data; std::vector expected_icons; std::vector expected_deletions; std::set favicon_urls_to_delete; for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData test_data = BuildFaviconData(i); if (i < kFaviconBatchSize/2) { expected_icons.push_back(i); expected_deletions.push_back(syncer::SyncChange::ACTION_DELETE); favicon_urls_to_delete.insert(test_data.icon_url); } sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(test_data, image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } SetUpInitialSync(initial_image_data, initial_tracking_data); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); EXPECT_TRUE(changes.empty()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); cache()->OnURLsDeleted(nullptr, false, false, history::URLRows(), favicon_urls_to_delete); EXPECT_EQ(static_cast(kFaviconBatchSize)/2, GetFaviconCount()); changes = processor()->GetAndResetChangeList(); ASSERT_EQ(changes.size(), static_cast(kFaviconBatchSize)); syncer::SyncChangeList changes_1, changes_2; for (size_t i = 0; i < kFaviconBatchSize/2; ++i) { changes_1.push_back(changes[i]); changes_2.push_back(changes[i + kFaviconBatchSize/2]); } VerifyChanges(syncer::FAVICON_IMAGES, expected_deletions, expected_icons, changes_1); VerifyChanges(syncer::FAVICON_TRACKING, expected_deletions, expected_icons, changes_2); } // Any favicon urls with the "data" scheme should be ignored. TEST_F(SyncFaviconCacheTest, IgnoreDataScheme) { EXPECT_EQ(0U, GetFaviconCount()); SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList()); std::vector expected_icons; for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData test_data = BuildFaviconData(i); cache()->OnFaviconVisited(test_data.page_url, GURL()); } EXPECT_EQ(static_cast(kFaviconBatchSize), GetTaskCount()); for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData test_data = BuildFaviconData(i); test_data.icon_url = GURL("data:image/png;base64;blabla"); EXPECT_TRUE(test_data.icon_url.is_valid()); OnCustomFaviconDataAvailable(test_data); } EXPECT_EQ(0U, GetTaskCount()); EXPECT_EQ(0U, GetFaviconCount()); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); EXPECT_TRUE(changes.empty()); } // When visiting a page we've already loaded the favicon for, don't attempt to // reload the favicon, just update the visit time using the cached icon url. TEST_F(SyncFaviconCacheTest, ReuseCachedIconUrl) { EXPECT_EQ(0U, GetFaviconCount()); SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList()); std::vector expected_icons; for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); TestFaviconData test_data = BuildFaviconData(i); cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url); } EXPECT_EQ(static_cast(kFaviconBatchSize), GetTaskCount()); for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData test_data = BuildFaviconData(i); OnCustomFaviconDataAvailable(test_data); } processor()->GetAndResetChangeList(); EXPECT_EQ(0U, GetTaskCount()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData test_data = BuildFaviconData(i); cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); ASSERT_EQ(1U, changes.size()); // Just verify the favicon url for the tracking specifics and that the // timestamp is non-null. EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type()); EXPECT_EQ(test_data.icon_url.spec(), changes[0].sync_data().GetSpecifics().favicon_tracking(). favicon_url()); EXPECT_NE(changes[0].sync_data().GetSpecifics().favicon_tracking(). last_visit_time_ms(), 0); } EXPECT_EQ(0U, GetTaskCount()); } // If we wind up with orphan image/tracking nodes, then receive an update // for those favicons, we should lazily create the missing nodes. TEST_F(SyncFaviconCacheTest, UpdatedOrphans) { EXPECT_EQ(0U, GetFaviconCount()); SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList()); syncer::SyncChangeList initial_image_changes; syncer::SyncChangeList initial_tracking_changes; for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData test_data = BuildFaviconData(i); // Even favicons have image data but no tracking data. Odd favicons have // tracking data but no image data. if (i % 2 == 0) { sync_pb::EntitySpecifics image_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_changes.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_ADD, syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); } else { sync_pb::EntitySpecifics tracking_specifics; FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_changes.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_ADD, syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create()))); } } cache()->ProcessSyncChanges(FROM_HERE, initial_image_changes); cache()->ProcessSyncChanges(FROM_HERE, initial_tracking_changes); EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); for (int i = 0; i < kFaviconBatchSize/2; ++i) { TestFaviconData test_data = BuildFaviconData(i); cache()->OnFaviconVisited(test_data.page_url, GURL()); EXPECT_EQ(1U, GetTaskCount()); OnCustomFaviconDataAvailable(test_data); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); // Even favicons had image data, so should now receive new tracking data // and updated image data (we allow one update after the initial add). // Odd favicons had tracking so should now receive new image data and // updated tracking data. if (i % 2 == 0) { ASSERT_EQ(2U, changes.size()); EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type()); EXPECT_TRUE( CompareFaviconDataToSpecifics(test_data, changes[0].sync_data().GetSpecifics())); EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[1].change_type()); EXPECT_EQ(test_data.icon_url.spec(), changes[1].sync_data().GetSpecifics().favicon_tracking(). favicon_url()); EXPECT_NE(changes[1].sync_data().GetSpecifics().favicon_tracking(). last_visit_time_ms(), 0); } else { ASSERT_EQ(2U, changes.size()); EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type()); EXPECT_TRUE( CompareFaviconDataToSpecifics(test_data, changes[0].sync_data().GetSpecifics())); EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[1].change_type()); EXPECT_EQ(test_data.icon_url.spec(), changes[1].sync_data().GetSpecifics().favicon_tracking(). favicon_url()); EXPECT_NE(changes[1].sync_data().GetSpecifics().favicon_tracking(). last_visit_time_ms(), 0); } } EXPECT_EQ(0U, GetTaskCount()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); } // Verify that orphaned favicon images don't result in creating invalid // favicon tracking data. TEST_F(SyncFaviconCacheTest, PartialAssociationInfo) { syncer::SyncDataList initial_image_data, initial_tracking_data; for (int i = 0; i < kFaviconBatchSize; ++i) { sync_pb::EntitySpecifics image_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); image_specifics.mutable_favicon_image()->clear_favicon_web(); } SetUpInitialSync(initial_image_data, initial_tracking_data); syncer::SyncChangeList change_list = processor()->GetAndResetChangeList(); EXPECT_TRUE(change_list.empty()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); } // Tests that we don't choke if a favicon visit node with a null visit time is // present (see crbug.com/258196) and an update is made. TEST_F(SyncFaviconCacheTest, NullFaviconVisitTime) { EXPECT_EQ(0U, GetFaviconCount()); syncer::SyncDataList initial_image_data, initial_tracking_data; std::vector expected_icons; for (int i = 0; i < kFaviconBatchSize; ++i) { expected_icons.push_back(i); sync_pb::EntitySpecifics image_specifics, tracking_specifics; FillImageSpecifics(BuildFaviconData(i), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); tracking_specifics.mutable_favicon_tracking()->set_last_visit_time_ms( syncer::TimeToProtoTime(base::Time())); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES, initial_image_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); ASSERT_EQ(0U, processor()->GetAndResetChangeList().size()); cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING, initial_tracking_data, CreateAndPassProcessor(), CreateAndPassSyncErrorFactory()); ASSERT_EQ(static_cast(kFaviconBatchSize), processor()->GetAndResetChangeList().size()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); // Visit the favicons again. EXPECT_EQ(0U, GetTaskCount()); for (int i = 0; i < kFaviconBatchSize; ++i) { TestFaviconData test_data = BuildFaviconData(i); cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); ASSERT_EQ(1U, changes.size()); // Just verify the favicon url for the tracking specifics and that the // timestamp is non-null. EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type()); EXPECT_EQ(test_data.icon_url.spec(), changes[0].sync_data().GetSpecifics().favicon_tracking(). favicon_url()); EXPECT_NE(changes[0].sync_data().GetSpecifics().favicon_tracking(). last_visit_time_ms(), 0); } EXPECT_EQ(0U, GetTaskCount()); EXPECT_EQ(static_cast(kFaviconBatchSize), GetFaviconCount()); } // If another synced client has a clock skewed towards the future, it's possible // that favicons added locally will be expired as they are added. Ensure this // doesn't crash (see crbug.com/306150). TEST_F(SyncFaviconCacheTest, VisitFaviconClockSkew) { EXPECT_EQ(0U, GetFaviconCount()); const int kClockSkew = 20; // 20 minutes in the future. // Set up sync with kMaxSyncFavicons starting kClockSkew minutes in the // future. syncer::SyncDataList initial_image_data, initial_tracking_data; for (int i = 0; i < kMaxSyncFavicons; ++i) { sync_pb::EntitySpecifics image_specifics, tracking_specifics; TestFaviconData test_data = BuildFaviconData(i); test_data.last_visit_time = syncer::TimeToProtoTime( base::Time::Now() + base::TimeDelta::FromMinutes(kClockSkew)); FillImageSpecifics(test_data, image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); FillTrackingSpecifics(test_data, tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } SetUpInitialSync(initial_image_data, initial_tracking_data); // Visit some new favicons with local time, which will be expired as they // are added. EXPECT_EQ(0U, GetTaskCount()); for (int i = 0; i < kClockSkew; ++i) { TestFaviconData test_data = BuildFaviconData(i + kMaxSyncFavicons); cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url); OnCustomFaviconDataAvailable(test_data); // The changes will be an add followed by a delete for both the image and // tracking info. syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); ASSERT_EQ(changes.size(), 4U); ASSERT_EQ(changes[0].change_type(), syncer::SyncChange::ACTION_ADD); ASSERT_EQ(changes[0].sync_data().GetDataType(), syncer::FAVICON_IMAGES); ASSERT_EQ(changes[1].change_type(), syncer::SyncChange::ACTION_DELETE); ASSERT_EQ(changes[1].sync_data().GetDataType(), syncer::FAVICON_IMAGES); ASSERT_EQ(changes[2].change_type(), syncer::SyncChange::ACTION_ADD); ASSERT_EQ(changes[2].sync_data().GetDataType(), syncer::FAVICON_TRACKING); ASSERT_EQ(changes[3].change_type(), syncer::SyncChange::ACTION_DELETE); ASSERT_EQ(changes[3].sync_data().GetDataType(), syncer::FAVICON_TRACKING); } EXPECT_EQ(0U, GetTaskCount()); EXPECT_EQ(static_cast(kMaxSyncFavicons), GetFaviconCount()); } // Simulate a case where the set of tracking info and image info doesn't match, // and there is more tracking info than the max. A local update should correctly // determine whether to update/add an image/tracking entity. TEST_F(SyncFaviconCacheTest, MixedThreshold) { // First go through and add local favicons. for (int i = kMaxSyncFavicons; i < kMaxSyncFavicons + 5; ++i) { TestFaviconData favicon = BuildFaviconData(i); TriggerSyncFaviconReceived(favicon.page_url, favicon.icon_url, favicon.image_16, favicon.last_visit_time); } syncer::SyncDataList initial_image_data, initial_tracking_data; // Then sync with enough favicons such that the tracking info is over the max // after merge completes. for (int i = 0; i < kMaxSyncFavicons; ++i) { sync_pb::EntitySpecifics image_specifics; // Push the images forward by 5, to match the unsynced favicons. FillImageSpecifics(BuildFaviconData(i + 5), image_specifics.mutable_favicon_image()); initial_image_data.push_back(syncer::SyncData::CreateRemoteData( 1, image_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); sync_pb::EntitySpecifics tracking_specifics; FillTrackingSpecifics(BuildFaviconData(i), tracking_specifics.mutable_favicon_tracking()); initial_tracking_data.push_back(syncer::SyncData::CreateRemoteData( 1, tracking_specifics, base::Time(), syncer::AttachmentIdList(), syncer::AttachmentServiceProxyForTest::Create())); } SetUpInitialSync(initial_image_data, initial_tracking_data); // The local unsynced tracking info should be dropped, but not deleted. EXPECT_EQ(0U, processor()->GetAndResetChangeList().size()); // Because the image and tracking data don't overlap, the total number of // favicons is still over the limit. EXPECT_EQ(static_cast(kMaxSyncFavicons)+5, GetFaviconCount()); // Trigger a tracking change for one of the favicons whose tracking info // was dropped, resulting in a tracking add and expiration of the orphaned // images. TestFaviconData test_data = BuildFaviconData(kMaxSyncFavicons); cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url); syncer::SyncChangeList changes = processor()->GetAndResetChangeList(); // 1 image update, 5 image deletions, 1 tracking deletion. ASSERT_EQ(6U, changes.size()); // Expire image for favicon[kMaxSyncFavicons + 1]. EXPECT_EQ(changes[0].change_type(), syncer::SyncChange::ACTION_DELETE); EXPECT_EQ(changes[0].sync_data().GetDataType(), syncer::FAVICON_IMAGES); EXPECT_EQ(kMaxSyncFavicons + 1, GetFaviconId(changes[0])); // Expire image for favicon[kMaxSyncFavicons + 2]. EXPECT_EQ(changes[1].change_type(), syncer::SyncChange::ACTION_DELETE); EXPECT_EQ(changes[1].sync_data().GetDataType(), syncer::FAVICON_IMAGES); EXPECT_EQ(kMaxSyncFavicons + 2, GetFaviconId(changes[1])); // Expire image for favicon[kMaxSyncFavicons + 3]. EXPECT_EQ(changes[2].change_type(), syncer::SyncChange::ACTION_DELETE); EXPECT_EQ(changes[2].sync_data().GetDataType(), syncer::FAVICON_IMAGES); EXPECT_EQ(kMaxSyncFavicons + 3, GetFaviconId(changes[2])); // Expire image for favicon[kMaxSyncFavicons + 4]. EXPECT_EQ(changes[3].change_type(), syncer::SyncChange::ACTION_DELETE); EXPECT_EQ(changes[3].sync_data().GetDataType(), syncer::FAVICON_IMAGES); EXPECT_EQ(kMaxSyncFavicons + 4, GetFaviconId(changes[3])); // Update tracking for favicon[kMaxSyncFavicons]. EXPECT_EQ(changes[4].change_type(), syncer::SyncChange::ACTION_ADD); EXPECT_EQ(changes[4].sync_data().GetDataType(), syncer::FAVICON_TRACKING); EXPECT_EQ(kMaxSyncFavicons, GetFaviconId(changes[4])); // Expire tracking for favicon[0]. EXPECT_EQ(changes[5].change_type(), syncer::SyncChange::ACTION_DELETE); EXPECT_EQ(changes[5].sync_data().GetDataType(), syncer::FAVICON_TRACKING); EXPECT_EQ(0, GetFaviconId(changes[5])); } } // namespace browser_sync