1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/sync/glue/favicon_cache.h"
6
7#include "base/message_loop/message_loop.h"
8#include "base/strings/stringprintf.h"
9#include "base/time/time.h"
10#include "chrome/browser/chrome_notification_types.h"
11#include "chrome/browser/history/history_notifications.h"
12#include "content/public/browser/notification_service.h"
13#include "sync/api/sync_error_factory_mock.h"
14#include "sync/api/time.h"
15#include "sync/protocol/favicon_image_specifics.pb.h"
16#include "sync/protocol/favicon_tracking_specifics.pb.h"
17#include "sync/protocol/sync.pb.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20namespace browser_sync {
21
22namespace {
23
24// Total number of favicons to use in sync test batches.
25const int kFaviconBatchSize = 10;
26
27// Maximum number of favicons to sync.
28const int kMaxSyncFavicons = kFaviconBatchSize*2;
29
30// TestChangeProcessor --------------------------------------------------------
31
32// Dummy SyncChangeProcessor used to help review what SyncChanges are pushed
33// back up to Sync.
34class TestChangeProcessor : public syncer::SyncChangeProcessor {
35 public:
36  TestChangeProcessor();
37  virtual ~TestChangeProcessor();
38
39  // Store a copy of all the changes passed in so we can examine them later.
40  virtual syncer::SyncError ProcessSyncChanges(
41      const tracked_objects::Location& from_here,
42      const syncer::SyncChangeList& change_list) OVERRIDE;
43
44  virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
45      OVERRIDE {
46    return syncer::SyncDataList();
47  }
48
49  bool contains_guid(const std::string& guid) const {
50    return change_map_.count(guid) != 0;
51  }
52
53  syncer::SyncChange change_for_guid(const std::string& guid) const {
54    DCHECK(contains_guid(guid));
55    return change_map_.find(guid)->second;
56  }
57
58  // Returns the last change list received, and resets the internal list.
59  syncer::SyncChangeList GetAndResetChangeList() {
60    syncer::SyncChangeList list;
61    list.swap(change_list_);
62    return list;
63  }
64
65  void set_erroneous(bool erroneous) { erroneous_ = erroneous; }
66
67 private:
68  // Track the changes received in ProcessSyncChanges.
69  std::map<std::string, syncer::SyncChange> change_map_;
70  syncer::SyncChangeList change_list_;
71  bool erroneous_;
72
73  DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor);
74};
75
76TestChangeProcessor::TestChangeProcessor() : erroneous_(false) {
77}
78
79TestChangeProcessor::~TestChangeProcessor() {
80}
81
82syncer::SyncError TestChangeProcessor::ProcessSyncChanges(
83    const tracked_objects::Location& from_here,
84    const syncer::SyncChangeList& change_list) {
85  if (erroneous_) {
86    return syncer::SyncError(
87        FROM_HERE,
88        syncer::SyncError::DATATYPE_ERROR,
89        "Some error.",
90        change_list[0].sync_data().GetDataType());
91  }
92
93  change_list_.insert(change_list_.end(),
94                      change_list.begin(),
95                      change_list.end());
96  change_map_.erase(change_map_.begin(), change_map_.end());
97  for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
98      iter != change_list.end(); ++iter) {
99    change_map_[iter->sync_data().GetTitle()] = *iter;
100  }
101  return syncer::SyncError();
102}
103
104
105// SyncChangeProcessorDelegate ------------------------------------------------
106
107class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor {
108 public:
109  explicit SyncChangeProcessorDelegate(syncer::SyncChangeProcessor* recipient);
110  virtual ~SyncChangeProcessorDelegate();
111
112  // syncer::SyncChangeProcessor implementation.
113  virtual syncer::SyncError ProcessSyncChanges(
114      const tracked_objects::Location& from_here,
115      const syncer::SyncChangeList& change_list) OVERRIDE;
116
117  virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
118      OVERRIDE {
119    return recipient_->GetAllSyncData(type);
120  }
121
122 private:
123  // The recipient of all sync changes.
124  syncer::SyncChangeProcessor* recipient_;
125
126  DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate);
127};
128
129SyncChangeProcessorDelegate::SyncChangeProcessorDelegate(
130    syncer::SyncChangeProcessor* recipient)
131    : recipient_(recipient) {
132  DCHECK(recipient_);
133}
134
135SyncChangeProcessorDelegate::~SyncChangeProcessorDelegate() {
136}
137
138syncer::SyncError SyncChangeProcessorDelegate::ProcessSyncChanges(
139    const tracked_objects::Location& from_here,
140    const syncer::SyncChangeList& change_list) {
141  return recipient_->ProcessSyncChanges(from_here, change_list);
142}
143
144// TestFaviconData ------------------------------------------------------------
145struct TestFaviconData {
146  TestFaviconData() : last_visit_time(0), is_bookmarked(false) {}
147  GURL page_url;
148  GURL icon_url;
149  std::string image_16;
150  std::string image_32;
151  std::string image_64;
152  int64 last_visit_time;
153  bool is_bookmarked;
154};
155
156TestFaviconData BuildFaviconData(int index) {
157  TestFaviconData data;
158  data.page_url = GURL(base::StringPrintf("http://bla.com/%.2i.html", index));
159  data.icon_url = GURL(base::StringPrintf("http://bla.com/%.2i.ico", index));
160  data.image_16 = base::StringPrintf("16 %i", index);
161  // TODO(zea): enable this once the cache supports writing them.
162  // data.image_32 = base::StringPrintf("32 %i", index);
163  // data.image_64 = base::StringPrintf("64 %i", index);
164  data.last_visit_time = index;
165  return data;
166}
167
168void FillImageSpecifics(
169    const TestFaviconData& test_data,
170    sync_pb::FaviconImageSpecifics* image_specifics) {
171  image_specifics->set_favicon_url(test_data.icon_url.spec());
172  if (!test_data.image_16.empty()) {
173    image_specifics->mutable_favicon_web()->set_height(16);
174    image_specifics->mutable_favicon_web()->set_width(16);
175    image_specifics->mutable_favicon_web()->set_favicon(test_data.image_16);
176  }
177  if (!test_data.image_32.empty()) {
178    image_specifics->mutable_favicon_web_32()->set_height(32);
179    image_specifics->mutable_favicon_web_32()->set_width(32);
180    image_specifics->mutable_favicon_web_32()->set_favicon(test_data.image_32);
181  }
182  if (!test_data.image_64.empty()) {
183    image_specifics->mutable_favicon_touch_64()->set_height(64);
184    image_specifics->mutable_favicon_touch_64()->set_width(64);
185    image_specifics->mutable_favicon_touch_64()->
186        set_favicon(test_data.image_64);
187  }
188}
189
190void FillTrackingSpecifics(
191    const TestFaviconData& test_data,
192    sync_pb::FaviconTrackingSpecifics* tracking_specifics) {
193  tracking_specifics->set_favicon_url(test_data.icon_url.spec());
194  tracking_specifics->set_last_visit_time_ms(test_data.last_visit_time);
195  tracking_specifics->set_is_bookmarked(test_data.is_bookmarked);
196}
197
198testing::AssertionResult CompareFaviconDataToSpecifics(
199    const TestFaviconData& test_data,
200    const sync_pb::EntitySpecifics& specifics) {
201  if (specifics.has_favicon_image()) {
202    sync_pb::FaviconImageSpecifics image_specifics = specifics.favicon_image();
203    if (image_specifics.favicon_url() != test_data.icon_url.spec())
204      return testing::AssertionFailure() << "Image icon url doesn't match.";
205    if (!test_data.image_16.empty()) {
206      if (image_specifics.favicon_web().favicon() != test_data.image_16 ||
207          image_specifics.favicon_web().height() != 16 ||
208          image_specifics.favicon_web().width() != 16) {
209        return testing::AssertionFailure() << "16p image data doesn't match.";
210      }
211    } else if (image_specifics.has_favicon_web()) {
212      return testing::AssertionFailure() << "Missing 16p favicon.";
213    }
214    if (!test_data.image_32.empty()) {
215      if (image_specifics.favicon_web_32().favicon() != test_data.image_32 ||
216          image_specifics.favicon_web().height() != 32 ||
217          image_specifics.favicon_web().width() != 32) {
218        return testing::AssertionFailure() << "32p image data doesn't match.";
219      }
220    } else if (image_specifics.has_favicon_web_32()) {
221      return testing::AssertionFailure() << "Missing 32p favicon.";
222    }
223    if (!test_data.image_64.empty()) {
224      if (image_specifics.favicon_touch_64().favicon() != test_data.image_64 ||
225          image_specifics.favicon_web().height() != 64 ||
226          image_specifics.favicon_web().width() != 64) {
227        return testing::AssertionFailure() << "64p image data doesn't match.";
228      }
229    } else if (image_specifics.has_favicon_touch_64()) {
230      return testing::AssertionFailure() << "Missing 64p favicon.";
231    }
232  } else {
233    sync_pb::FaviconTrackingSpecifics tracking_specifics =
234        specifics.favicon_tracking();
235    if (tracking_specifics.favicon_url() != test_data.icon_url.spec())
236      return testing::AssertionFailure() << "Tracking icon url doesn't match.";
237    if (tracking_specifics.last_visit_time_ms() != test_data.last_visit_time)
238      return testing::AssertionFailure() << "Visit time doesn't match.";
239    if (tracking_specifics.is_bookmarked() != test_data.is_bookmarked)
240      return testing::AssertionFailure() << "Bookmark status doens't match.";
241  }
242  return testing::AssertionSuccess();
243}
244
245testing::AssertionResult VerifyChanges(
246    syncer::ModelType expected_model_type,
247    const std::vector<syncer::SyncChange::SyncChangeType>&
248        expected_change_types,
249    const std::vector<int>& expected_icons,
250    const syncer::SyncChangeList& change_list) {
251  DCHECK_EQ(expected_change_types.size(), expected_icons.size());
252  if (change_list.size() != expected_icons.size())
253    return testing::AssertionFailure() << "Change list size doesn't match.";
254  for (size_t i = 0; i < expected_icons.size(); ++i) {
255    TestFaviconData data = BuildFaviconData(expected_icons[i]);
256    if (change_list[i].sync_data().GetDataType() != expected_model_type)
257      return testing::AssertionFailure() << "Change datatype doesn't match.";
258    if (change_list[i].change_type() != expected_change_types[i])
259      return testing::AssertionFailure() << "Change type doesn't match.";
260    if (change_list[i].change_type() == syncer::SyncChange::ACTION_DELETE) {
261      if (change_list[i].sync_data().GetTag() != data.icon_url.spec())
262        return testing::AssertionFailure() << "Deletion url does not match.";
263    } else {
264      testing::AssertionResult compare_result =
265          CompareFaviconDataToSpecifics(
266              data,
267              change_list[i].sync_data().GetSpecifics());
268      if (!compare_result)
269        return compare_result;
270    }
271  }
272  return testing::AssertionSuccess();
273}
274
275}  // namespace
276
277class SyncFaviconCacheTest : public testing::Test {
278 public:
279  SyncFaviconCacheTest();
280  virtual ~SyncFaviconCacheTest() {}
281
282  void SetUpInitialSync(const syncer::SyncDataList& initial_image_data,
283                        const syncer::SyncDataList& initial_tracking_data);
284
285  size_t GetFaviconCount() const;
286  size_t GetTaskCount() const;
287
288  testing::AssertionResult ExpectFaviconEquals(
289        const std::string& page_url,
290        const std::string& bytes) const;
291  testing::AssertionResult VerifyLocalIcons(
292      const std::vector<int>& expected_icons);
293  testing::AssertionResult VerifyLocalCustomIcons(
294      const std::vector<TestFaviconData>& expected_icons);
295
296  scoped_ptr<syncer::SyncChangeProcessor> CreateAndPassProcessor();
297  scoped_ptr<syncer::SyncErrorFactory> CreateAndPassSyncErrorFactory();
298
299  FaviconCache* cache() { return &cache_; }
300  TestChangeProcessor* processor() { return sync_processor_.get(); }
301
302  // Finish an outstanding favicon load for the icon described in |test_data|.
303  void OnCustomFaviconDataAvailable(const TestFaviconData& test_data);
304
305  // Helper method to run the message loop after invoking
306  // OnReceivedSyncFavicon, which posts an internal task.
307  void TriggerSyncFaviconReceived(const GURL& page_url,
308                                  const GURL& icon_url,
309                                  const std::string& icon_bytes,
310                                  int64 last_visit_time_ms);
311
312 private:
313  base::MessageLoopForUI message_loop_;
314  FaviconCache cache_;
315
316  // Our dummy ChangeProcessor used to inspect changes pushed to Sync.
317  scoped_ptr<TestChangeProcessor> sync_processor_;
318  scoped_ptr<SyncChangeProcessorDelegate> sync_processor_delegate_;
319};
320
321SyncFaviconCacheTest::SyncFaviconCacheTest()
322    : cache_(NULL, kMaxSyncFavicons),
323      sync_processor_(new TestChangeProcessor),
324      sync_processor_delegate_(new SyncChangeProcessorDelegate(
325                                   sync_processor_.get())) {
326}
327
328void SyncFaviconCacheTest::SetUpInitialSync(
329    const syncer::SyncDataList& initial_image_data,
330    const syncer::SyncDataList& initial_tracking_data) {
331  cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
332                                    initial_image_data,
333                                    CreateAndPassProcessor(),
334                                    CreateAndPassSyncErrorFactory());
335  ASSERT_EQ(0U, processor()->GetAndResetChangeList().size());
336  cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
337                                    initial_tracking_data,
338                                    CreateAndPassProcessor(),
339                                    CreateAndPassSyncErrorFactory());
340  ASSERT_EQ(0U, processor()->GetAndResetChangeList().size());
341}
342
343size_t SyncFaviconCacheTest::GetFaviconCount() const {
344  return cache_.NumFaviconsForTest();
345}
346
347size_t SyncFaviconCacheTest::GetTaskCount() const {
348  return cache_.NumTasksForTest();
349}
350
351testing::AssertionResult SyncFaviconCacheTest::ExpectFaviconEquals(
352    const std::string& page_url,
353    const std::string& bytes) const {
354  GURL gurl(page_url);
355  scoped_refptr<base::RefCountedMemory> favicon;
356  if (!cache_.GetSyncedFaviconForPageURL(gurl, &favicon))
357    return testing::AssertionFailure() << "Favicon is missing.";
358  if (favicon->size() != bytes.size())
359    return testing::AssertionFailure() << "Favicon sizes don't match.";
360  for (size_t i = 0; i < favicon->size(); ++i) {
361    if (bytes[i] != *(favicon->front() + i))
362      return testing::AssertionFailure() << "Favicon data doesn't match.";
363  }
364  return testing::AssertionSuccess();
365}
366
367testing::AssertionResult SyncFaviconCacheTest::VerifyLocalIcons(
368    const std::vector<int>& expected_icons) {
369  std::vector<TestFaviconData> expected_custom_icons;
370  for (size_t i = 0; i < expected_icons.size(); ++i) {
371    expected_custom_icons.push_back(BuildFaviconData(expected_icons[i]));
372  }
373  return VerifyLocalCustomIcons(expected_custom_icons);
374}
375
376
377testing::AssertionResult SyncFaviconCacheTest::VerifyLocalCustomIcons(
378    const std::vector<TestFaviconData>& expected_custom_icons) {
379  syncer::SyncDataList image_data_list =
380      cache()->GetAllSyncData(syncer::FAVICON_IMAGES);
381  syncer::SyncDataList tracking_data_list =
382      cache()->GetAllSyncData(syncer::FAVICON_TRACKING);
383  if (expected_custom_icons.size() > image_data_list.size() ||
384      expected_custom_icons.size() > tracking_data_list.size())
385    return testing::AssertionFailure() << "Number of icons doesn't match.";
386  for (size_t i = 0; i < expected_custom_icons.size(); ++i) {
387    const TestFaviconData& test_data = expected_custom_icons[i];
388    // Find the test data in the data lists. Assume that both lists have the
389    // same ordering, which may not match the |expected_custom_icons| ordering.
390    bool found_match = false;
391    for (size_t j = 0; j < image_data_list.size(); ++j) {
392      if (image_data_list[j].GetTitle() != test_data.icon_url.spec())
393        continue;
394      found_match = true;
395      const sync_pb::FaviconImageSpecifics& image_specifics =
396          image_data_list[j].GetSpecifics().favicon_image();
397      sync_pb::FaviconImageSpecifics expected_image_specifics;
398      FillImageSpecifics(test_data, &expected_image_specifics);
399      if (image_specifics.SerializeAsString() !=
400          expected_image_specifics.SerializeAsString()) {
401        return testing::AssertionFailure() << "Image data doesn't match.";
402      }
403      const sync_pb::FaviconTrackingSpecifics& tracking_specifics =
404          tracking_data_list[j].GetSpecifics().favicon_tracking();
405      sync_pb::FaviconTrackingSpecifics expected_tracking_specifics;
406      FillTrackingSpecifics(test_data, &expected_tracking_specifics);
407      if (tracking_specifics.SerializeAsString() !=
408          expected_tracking_specifics.SerializeAsString()) {
409        return testing::AssertionFailure() << "Tracking data doesn't match.";
410      }
411    }
412    if (!found_match)
413      return testing::AssertionFailure() << "Could not find favicon.";
414  }
415  return testing::AssertionSuccess();
416}
417
418scoped_ptr<syncer::SyncChangeProcessor>
419SyncFaviconCacheTest::CreateAndPassProcessor() {
420  return scoped_ptr<syncer::SyncChangeProcessor>(
421      new SyncChangeProcessorDelegate(sync_processor_.get()));
422}
423
424scoped_ptr<syncer::SyncErrorFactory> SyncFaviconCacheTest::
425    CreateAndPassSyncErrorFactory() {
426  return scoped_ptr<syncer::SyncErrorFactory>(
427      new syncer::SyncErrorFactoryMock());
428}
429
430void SyncFaviconCacheTest::OnCustomFaviconDataAvailable(
431    const TestFaviconData& test_data) {
432  std::vector<chrome::FaviconBitmapResult> bitmap_results;
433  if (!test_data.image_16.empty()) {
434    chrome::FaviconBitmapResult bitmap_result;
435    bitmap_result.icon_url = test_data.icon_url;
436    bitmap_result.pixel_size.set_width(16);
437    bitmap_result.pixel_size.set_height(16);
438    base::RefCountedString* temp_string = new base::RefCountedString();
439    temp_string->data() = test_data.image_16;
440    bitmap_result.bitmap_data = temp_string;
441    bitmap_results.push_back(bitmap_result);
442  }
443  if (!test_data.image_32.empty()) {
444    chrome::FaviconBitmapResult bitmap_result;
445    bitmap_result.icon_url = test_data.icon_url;
446    bitmap_result.pixel_size.set_width(32);
447    bitmap_result.pixel_size.set_height(32);
448    base::RefCountedString* temp_string = new base::RefCountedString();
449    temp_string->data() = test_data.image_32;
450    bitmap_result.bitmap_data = temp_string;
451    bitmap_results.push_back(bitmap_result);
452  }
453  if (!test_data.image_64.empty()) {
454    chrome::FaviconBitmapResult bitmap_result;
455    bitmap_result.icon_url = test_data.icon_url;
456    bitmap_result.pixel_size.set_width(64);
457    bitmap_result.pixel_size.set_height(64);
458    base::RefCountedString* temp_string = new base::RefCountedString();
459    temp_string->data() = test_data.image_64;
460    bitmap_result.bitmap_data = temp_string;
461    bitmap_results.push_back(bitmap_result);
462  }
463  cache()->OnFaviconDataAvailable(test_data.page_url, bitmap_results);
464}
465
466void SyncFaviconCacheTest::TriggerSyncFaviconReceived(
467    const GURL& page_url,
468    const GURL& icon_url,
469    const std::string& icon_bytes,
470    int64 last_visit_time_ms) {
471  cache()->OnReceivedSyncFavicon(page_url,
472                                 icon_url,
473                                 icon_bytes,
474                                 last_visit_time_ms);
475  message_loop_.RunUntilIdle();
476}
477
478// A freshly constructed cache should be empty.
479TEST_F(SyncFaviconCacheTest, Empty) {
480  EXPECT_EQ(0U, GetFaviconCount());
481}
482
483TEST_F(SyncFaviconCacheTest, ReceiveSyncFavicon) {
484  std::string page_url = "http://www.google.com";
485  std::string fav_url = "http://www.google.com/favicon.ico";
486  std::string bytes = "bytes";
487  EXPECT_EQ(0U, GetFaviconCount());
488  TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), bytes, 0);
489  EXPECT_EQ(1U, GetFaviconCount());
490  EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes));
491}
492
493TEST_F(SyncFaviconCacheTest, ReceiveEmptySyncFavicon) {
494  std::string page_url = "http://www.google.com";
495  std::string fav_url = "http://www.google.com/favicon.ico";
496  std::string bytes = "bytes";
497  EXPECT_EQ(0U, GetFaviconCount());
498  TriggerSyncFaviconReceived(GURL(page_url),
499                             GURL(fav_url),
500                             std::string(),
501                             0);
502  EXPECT_EQ(0U, GetFaviconCount());
503  EXPECT_FALSE(ExpectFaviconEquals(page_url, std::string()));
504
505  // Then receive the actual favicon.
506  TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), bytes, 0);
507  EXPECT_EQ(1U, GetFaviconCount());
508  EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes));
509}
510
511TEST_F(SyncFaviconCacheTest, ReceiveUpdatedSyncFavicon) {
512  std::string page_url = "http://www.google.com";
513  std::string fav_url = "http://www.google.com/favicon.ico";
514  std::string bytes = "bytes";
515  std::string bytes2 = "bytes2";
516  EXPECT_EQ(0U, GetFaviconCount());
517  TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), bytes, 0);
518  EXPECT_EQ(1U, GetFaviconCount());
519  EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes));
520
521  // The cache should not update existing favicons from tab sync favicons
522  // (which can be reassociated several times).
523  TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), bytes2, 0);
524  EXPECT_EQ(1U, GetFaviconCount());
525  EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes));
526  EXPECT_FALSE(ExpectFaviconEquals(page_url, bytes2));
527}
528
529TEST_F(SyncFaviconCacheTest, MultipleMappings) {
530  std::string page_url = "http://www.google.com";
531  std::string page2_url = "http://bla.google.com";
532  std::string fav_url = "http://www.google.com/favicon.ico";
533  std::string bytes = "bytes";
534  EXPECT_EQ(0U, GetFaviconCount());
535  TriggerSyncFaviconReceived(GURL(page_url), GURL(fav_url), bytes, 0);
536  EXPECT_EQ(1U, GetFaviconCount());
537  EXPECT_TRUE(ExpectFaviconEquals(page_url, bytes));
538
539  // Map another page to the same favicon. They should share the same data.
540  TriggerSyncFaviconReceived(GURL(page2_url), GURL(fav_url), bytes, 0);
541  EXPECT_EQ(1U, GetFaviconCount());
542  EXPECT_TRUE(ExpectFaviconEquals(page2_url, bytes));
543}
544
545TEST_F(SyncFaviconCacheTest, SyncEmpty) {
546  syncer::SyncMergeResult merge_result =
547      cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
548                                        syncer::SyncDataList(),
549                                        CreateAndPassProcessor(),
550                                        CreateAndPassSyncErrorFactory());
551
552  EXPECT_EQ(0U, cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
553  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
554  EXPECT_EQ(0, merge_result.num_items_added());
555  EXPECT_EQ(0, merge_result.num_items_modified());
556  EXPECT_EQ(0, merge_result.num_items_deleted());
557  EXPECT_EQ(0, merge_result.num_items_before_association());
558  EXPECT_EQ(0, merge_result.num_items_after_association());
559
560  merge_result =
561      cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
562                                        syncer::SyncDataList(),
563                                        CreateAndPassProcessor(),
564                                        CreateAndPassSyncErrorFactory());
565
566  EXPECT_EQ(0U, cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
567  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
568  EXPECT_EQ(0, merge_result.num_items_added());
569  EXPECT_EQ(0, merge_result.num_items_modified());
570  EXPECT_EQ(0, merge_result.num_items_deleted());
571  EXPECT_EQ(0, merge_result.num_items_before_association());
572  EXPECT_EQ(0, merge_result.num_items_after_association());
573}
574
575// Setting up sync with existing local favicons should push those favicons into
576// sync.
577TEST_F(SyncFaviconCacheTest, SyncExistingLocal) {
578  std::vector<syncer::SyncChange::SyncChangeType> expected_change_types;
579  std::vector<int> expected_icons;
580  for (int i = 0; i < kFaviconBatchSize; ++i) {
581    TestFaviconData favicon = BuildFaviconData(i);
582    TriggerSyncFaviconReceived(favicon.page_url,
583                               favicon.icon_url,
584                               favicon.image_16,
585                               i);
586    expected_change_types.push_back(syncer::SyncChange::ACTION_ADD);
587    expected_icons.push_back(i);
588  }
589
590  syncer::SyncMergeResult merge_result =
591      cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
592                                        syncer::SyncDataList(),
593                                        CreateAndPassProcessor(),
594                                        CreateAndPassSyncErrorFactory());
595  EXPECT_EQ((unsigned long)kFaviconBatchSize,
596            cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
597  syncer::SyncChangeList change_list = processor()->GetAndResetChangeList();
598  EXPECT_TRUE(VerifyChanges(syncer::FAVICON_IMAGES,
599                            expected_change_types,
600                            expected_icons,
601                            change_list));
602  EXPECT_EQ(0, merge_result.num_items_added());
603  EXPECT_EQ(0, merge_result.num_items_modified());
604  EXPECT_EQ(0, merge_result.num_items_deleted());
605  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association());
606  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association());
607
608  merge_result =
609      cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
610                                        syncer::SyncDataList(),
611                                        CreateAndPassProcessor(),
612                                        CreateAndPassSyncErrorFactory());
613  EXPECT_EQ((unsigned long)kFaviconBatchSize,
614            cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
615  change_list = processor()->GetAndResetChangeList();
616  EXPECT_TRUE(VerifyChanges(syncer::FAVICON_TRACKING,
617                            expected_change_types,
618                            expected_icons,
619                            change_list));
620  EXPECT_EQ(0, merge_result.num_items_added());
621  EXPECT_EQ(0, merge_result.num_items_modified());
622  EXPECT_EQ(0, merge_result.num_items_deleted());
623  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association());
624  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association());
625}
626
627// Setting up sync with existing sync data should load that data into the local
628// cache.
629TEST_F(SyncFaviconCacheTest, SyncExistingRemote) {
630  syncer::SyncDataList initial_image_data, initial_tracking_data;
631  std::vector<int> expected_icons;
632  for (int i = 0; i < kFaviconBatchSize; ++i) {
633    expected_icons.push_back(i);
634    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
635    FillImageSpecifics(BuildFaviconData(i),
636                       image_specifics.mutable_favicon_image());
637    initial_image_data.push_back(
638        syncer::SyncData::CreateRemoteData(1,
639                                           image_specifics,
640                                           base::Time()));
641    FillTrackingSpecifics(BuildFaviconData(i),
642                          tracking_specifics.mutable_favicon_tracking());
643    initial_tracking_data.push_back(
644        syncer::SyncData::CreateRemoteData(1,
645                                           tracking_specifics,
646                                           base::Time()));
647  }
648
649  syncer::SyncMergeResult merge_result =
650      cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
651                                        initial_image_data,
652                                        CreateAndPassProcessor(),
653                                        CreateAndPassSyncErrorFactory());
654  EXPECT_EQ((unsigned long)kFaviconBatchSize,
655            cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
656  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
657  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_added());
658  EXPECT_EQ(0, merge_result.num_items_modified());
659  EXPECT_EQ(0, merge_result.num_items_deleted());
660  EXPECT_EQ(0, merge_result.num_items_before_association());
661  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association());
662
663  merge_result =
664      cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
665                                        initial_tracking_data,
666                                        CreateAndPassProcessor(),
667                                        CreateAndPassSyncErrorFactory());
668  EXPECT_EQ((unsigned long)kFaviconBatchSize,
669            cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
670  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
671  EXPECT_EQ(0, merge_result.num_items_added());
672  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_modified());
673  EXPECT_EQ(0, merge_result.num_items_deleted());
674  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association());
675  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association());
676
677  ASSERT_TRUE(VerifyLocalIcons(expected_icons));
678}
679
680// Setting up sync with local data and sync data should merge the two image
681// sets, with remote data having priority in case both exist.
682TEST_F(SyncFaviconCacheTest, SyncMergesImages) {
683  // First go through and add local 16p favicons.
684  for (int i = 0; i < kFaviconBatchSize; ++i) {
685    TestFaviconData favicon = BuildFaviconData(i);
686    TriggerSyncFaviconReceived(favicon.page_url,
687                               favicon.icon_url,
688                               favicon.image_16,
689                               i);
690  }
691
692  // Then go through and create the initial sync data, which does not have 16p
693  // favicons for the first half, and has custom 16p favicons for the second.
694  std::vector<syncer::SyncChange::SyncChangeType> expected_change_types;
695  std::vector<int> expected_icons;
696  std::vector<TestFaviconData> expected_data;
697  syncer::SyncDataList initial_image_data, initial_tracking_data;
698  for (int i = 0; i < kFaviconBatchSize; ++i) {
699    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
700    TestFaviconData test_data = BuildFaviconData(i);
701    if (i < kFaviconBatchSize/2) {
702      test_data.image_16 = std::string();
703      expected_icons.push_back(i);
704      expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE);
705    } else {
706      test_data.image_16 += "custom";
707      expected_data.push_back(test_data);
708    }
709    FillImageSpecifics(test_data,
710                       image_specifics.mutable_favicon_image());
711
712    initial_image_data.push_back(
713        syncer::SyncData::CreateRemoteData(1,
714                                           image_specifics,
715                                           base::Time()));
716    FillTrackingSpecifics(test_data,
717                          tracking_specifics.mutable_favicon_tracking());
718    initial_tracking_data.push_back(
719        syncer::SyncData::CreateRemoteData(1,
720                                           tracking_specifics,
721                                           base::Time()));
722  }
723
724  syncer::SyncMergeResult merge_result =
725      cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
726                                        initial_image_data,
727                                        CreateAndPassProcessor(),
728                                        CreateAndPassSyncErrorFactory());
729  EXPECT_EQ((unsigned long)kFaviconBatchSize,
730            cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
731  syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
732  EXPECT_EQ((unsigned long)kFaviconBatchSize/2, changes.size());
733  EXPECT_EQ(0, merge_result.num_items_added());
734  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_modified());
735  EXPECT_EQ(0, merge_result.num_items_deleted());
736  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association());
737  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association());
738
739  merge_result =
740      cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
741                                        initial_tracking_data,
742                                        CreateAndPassProcessor(),
743                                        CreateAndPassSyncErrorFactory());
744  EXPECT_EQ((unsigned long)kFaviconBatchSize,
745            cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
746  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
747  EXPECT_EQ(0, merge_result.num_items_added());
748  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_modified());
749  EXPECT_EQ(0, merge_result.num_items_deleted());
750  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association());
751  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association());
752
753  ASSERT_TRUE(VerifyLocalIcons(expected_icons));
754  ASSERT_TRUE(VerifyLocalCustomIcons(expected_data));
755  ASSERT_TRUE(VerifyChanges(syncer::FAVICON_IMAGES,
756                            expected_change_types,
757                            expected_icons,
758                            changes));
759}
760
761// Setting up sync with local data and sync data should merge the two tracking
762// sets, such that the visit time is the most recent.
763TEST_F(SyncFaviconCacheTest, SyncMergesTracking) {
764  // First go through and add local 16p favicons.
765  for (int i = 0; i < kFaviconBatchSize; ++i) {
766    TestFaviconData favicon = BuildFaviconData(i);
767    TriggerSyncFaviconReceived(favicon.page_url,
768                               favicon.icon_url,
769                               favicon.image_16,
770                               i);
771  }
772
773  // Then go through and create the initial sync data, which for the first half
774  // the local has a newer visit, and for the second the remote does.
775  std::vector<syncer::SyncChange::SyncChangeType> expected_change_types;
776  std::vector<int> expected_icons;
777  std::vector<TestFaviconData> expected_data;
778  syncer::SyncDataList initial_image_data, initial_tracking_data;
779  for (int i = 0; i < kFaviconBatchSize; ++i) {
780    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
781    TestFaviconData test_data = BuildFaviconData(i);
782    if (i < kFaviconBatchSize/2) {
783      test_data.last_visit_time = i-1;
784      expected_icons.push_back(i);
785      expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE);
786    } else {
787      test_data.last_visit_time = i+1;
788      expected_data.push_back(test_data);
789    }
790    FillImageSpecifics(test_data,
791                       image_specifics.mutable_favicon_image());
792
793    initial_image_data.push_back(
794        syncer::SyncData::CreateRemoteData(1,
795                                           image_specifics,
796                                           base::Time()));
797    FillTrackingSpecifics(test_data,
798                          tracking_specifics.mutable_favicon_tracking());
799    initial_tracking_data.push_back(
800        syncer::SyncData::CreateRemoteData(1,
801                                           tracking_specifics,
802                                           base::Time()));
803  }
804
805  syncer::SyncMergeResult merge_result =
806      cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
807                                        initial_image_data,
808                                        CreateAndPassProcessor(),
809                                        CreateAndPassSyncErrorFactory());
810  EXPECT_EQ((unsigned long)kFaviconBatchSize,
811            cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
812  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
813  EXPECT_EQ(0, merge_result.num_items_added());
814  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_modified());
815  EXPECT_EQ(0, merge_result.num_items_deleted());
816  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association());
817  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association());
818
819  merge_result =
820      cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
821                                        initial_tracking_data,
822                                        CreateAndPassProcessor(),
823                                        CreateAndPassSyncErrorFactory());
824  EXPECT_EQ((unsigned long)kFaviconBatchSize,
825            cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
826  syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
827  EXPECT_EQ((unsigned long)kFaviconBatchSize/2, changes.size());
828  EXPECT_EQ(0, merge_result.num_items_added());
829  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_modified());
830  EXPECT_EQ(0, merge_result.num_items_deleted());
831  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_before_association());
832  EXPECT_EQ(kFaviconBatchSize, merge_result.num_items_after_association());
833
834  ASSERT_TRUE(VerifyLocalIcons(expected_icons));
835  ASSERT_TRUE(VerifyLocalCustomIcons(expected_data));
836  ASSERT_TRUE(VerifyChanges(syncer::FAVICON_TRACKING,
837                            expected_change_types,
838                            expected_icons,
839                            changes));
840}
841
842// Receiving old icons (missing image data) should result in pushing the new
843// merged icons back to the remote syncer.
844TEST_F(SyncFaviconCacheTest, ReceiveStaleImages) {
845  syncer::SyncDataList initial_image_data, initial_tracking_data;
846  syncer::SyncChangeList stale_changes;
847  std::vector<int> expected_icons;
848  std::vector<syncer::SyncChange::SyncChangeType> expected_change_types;
849  for (int i = 0; i < kFaviconBatchSize; ++i) {
850    expected_icons.push_back(i);
851    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
852    FillImageSpecifics(BuildFaviconData(i),
853                       image_specifics.mutable_favicon_image());
854    initial_image_data.push_back(
855        syncer::SyncData::CreateRemoteData(1,
856                                           image_specifics,
857                                           base::Time()));
858    expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE);
859    image_specifics.mutable_favicon_image()->clear_favicon_web();
860    stale_changes.push_back(
861        syncer::SyncChange(
862             FROM_HERE,
863             syncer::SyncChange::ACTION_UPDATE,
864             syncer::SyncData::CreateRemoteData(1,
865                                                image_specifics,
866                                                base::Time())));
867    FillTrackingSpecifics(BuildFaviconData(i),
868                          tracking_specifics.mutable_favicon_tracking());
869    initial_tracking_data.push_back(
870        syncer::SyncData::CreateRemoteData(1,
871                                           tracking_specifics,
872                                           base::Time()));
873  }
874
875  SetUpInitialSync(initial_image_data, initial_tracking_data);
876
877  // Now receive the same icons as an update, but with missing image data.
878  cache()->ProcessSyncChanges(FROM_HERE, stale_changes);
879  syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
880  ASSERT_TRUE(VerifyLocalIcons(expected_icons));
881  ASSERT_EQ((unsigned long)kFaviconBatchSize, changes.size());
882  ASSERT_TRUE(VerifyChanges(syncer::FAVICON_IMAGES,
883                            expected_change_types,
884                            expected_icons,
885                            changes));
886}
887
888// New icons should be added locally without pushing anything back to the
889// remote syncer.
890TEST_F(SyncFaviconCacheTest, ReceiveNewImages) {
891  syncer::SyncDataList initial_image_data, initial_tracking_data;
892  syncer::SyncChangeList new_changes;
893  std::vector<int> expected_icons;
894  for (int i = 0; i < kFaviconBatchSize; ++i) {
895    expected_icons.push_back(i);
896    TestFaviconData test_data = BuildFaviconData(i);
897    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
898    FillImageSpecifics(test_data,
899                       image_specifics.mutable_favicon_image());
900    new_changes.push_back(
901        syncer::SyncChange(
902             FROM_HERE,
903             syncer::SyncChange::ACTION_UPDATE,
904             syncer::SyncData::CreateRemoteData(1,
905                                                image_specifics,
906                                                base::Time())));
907    image_specifics.mutable_favicon_image()->mutable_favicon_web()->
908        mutable_favicon()->append("old");
909    initial_image_data.push_back(
910        syncer::SyncData::CreateRemoteData(1,
911                                           image_specifics,
912                                           base::Time()));
913    FillTrackingSpecifics(BuildFaviconData(i),
914                          tracking_specifics.mutable_favicon_tracking());
915    initial_tracking_data.push_back(
916        syncer::SyncData::CreateRemoteData(1,
917                                           tracking_specifics,
918                                           base::Time()));
919  }
920
921  SetUpInitialSync(initial_image_data, initial_tracking_data);
922
923  // Now receive the new icons as an update.
924  cache()->ProcessSyncChanges(FROM_HERE, new_changes);
925  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
926  ASSERT_TRUE(VerifyLocalIcons(expected_icons));
927}
928
929// Recieving the same icons as the local data should have no effect.
930TEST_F(SyncFaviconCacheTest, ReceiveSameImages) {
931  syncer::SyncDataList initial_image_data, initial_tracking_data;
932  syncer::SyncChangeList same_changes;
933  std::vector<int> expected_icons;
934  for (int i = 0; i < kFaviconBatchSize; ++i) {
935    expected_icons.push_back(i);
936    TestFaviconData test_data = BuildFaviconData(i);
937    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
938    FillImageSpecifics(test_data,
939                       image_specifics.mutable_favicon_image());
940    same_changes.push_back(
941        syncer::SyncChange(
942             FROM_HERE,
943             syncer::SyncChange::ACTION_UPDATE,
944             syncer::SyncData::CreateRemoteData(1,
945                                                image_specifics,
946                                                base::Time())));
947    initial_image_data.push_back(
948        syncer::SyncData::CreateRemoteData(1,
949                                           image_specifics,
950                                           base::Time()));
951    FillTrackingSpecifics(BuildFaviconData(i),
952                          tracking_specifics.mutable_favicon_tracking());
953    initial_tracking_data.push_back(
954        syncer::SyncData::CreateRemoteData(1,
955                                           tracking_specifics,
956                                           base::Time()));
957  }
958
959  SetUpInitialSync(initial_image_data, initial_tracking_data);
960
961  // Now receive the new icons as an update.
962  cache()->ProcessSyncChanges(FROM_HERE, same_changes);
963  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
964  ASSERT_TRUE(VerifyLocalIcons(expected_icons));
965}
966
967// Receiving stale tracking (old visit times) should result in pushing back
968// the newer visit times to the remote syncer.
969TEST_F(SyncFaviconCacheTest, ReceiveStaleTracking) {
970  syncer::SyncDataList initial_image_data, initial_tracking_data;
971  syncer::SyncChangeList stale_changes;
972  std::vector<int> expected_icons;
973  std::vector<syncer::SyncChange::SyncChangeType> expected_change_types;
974  for (int i = 0; i < kFaviconBatchSize; ++i) {
975    expected_icons.push_back(i);
976    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
977    FillImageSpecifics(BuildFaviconData(i),
978                       image_specifics.mutable_favicon_image());
979    initial_image_data.push_back(
980        syncer::SyncData::CreateRemoteData(1,
981                                           image_specifics,
982                                           base::Time()));
983    expected_change_types.push_back(syncer::SyncChange::ACTION_UPDATE);
984    FillTrackingSpecifics(BuildFaviconData(i),
985                          tracking_specifics.mutable_favicon_tracking());
986    initial_tracking_data.push_back(
987        syncer::SyncData::CreateRemoteData(1,
988                                           tracking_specifics,
989                                           base::Time()));
990    tracking_specifics.mutable_favicon_tracking()->set_last_visit_time_ms(-1);
991    stale_changes.push_back(
992        syncer::SyncChange(
993             FROM_HERE,
994             syncer::SyncChange::ACTION_UPDATE,
995             syncer::SyncData::CreateRemoteData(1,
996                                                tracking_specifics,
997                                                base::Time())));
998  }
999
1000  SetUpInitialSync(initial_image_data, initial_tracking_data);
1001
1002  // Now receive the same icons as an update, but with missing image data.
1003  cache()->ProcessSyncChanges(FROM_HERE, stale_changes);
1004  syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1005  ASSERT_TRUE(VerifyLocalIcons(expected_icons));
1006  ASSERT_EQ((unsigned long)kFaviconBatchSize, changes.size());
1007  ASSERT_TRUE(VerifyChanges(syncer::FAVICON_TRACKING,
1008                            expected_change_types,
1009                            expected_icons,
1010                            changes));
1011}
1012
1013// New tracking information should be added locally without pushing anything
1014// back to the remote syncer.
1015TEST_F(SyncFaviconCacheTest, ReceiveNewTracking) {
1016  syncer::SyncDataList initial_image_data, initial_tracking_data;
1017  syncer::SyncChangeList new_changes;
1018  std::vector<int> expected_icons;
1019  // We start from one here so that we don't have to deal with a -1 visit time.
1020  for (int i = 1; i <= kFaviconBatchSize; ++i) {
1021    expected_icons.push_back(i);
1022    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
1023    FillImageSpecifics(BuildFaviconData(i),
1024                       image_specifics.mutable_favicon_image());
1025    initial_image_data.push_back(
1026        syncer::SyncData::CreateRemoteData(1,
1027                                           image_specifics,
1028                                           base::Time()));
1029    FillTrackingSpecifics(BuildFaviconData(i),
1030                          tracking_specifics.mutable_favicon_tracking());
1031    new_changes.push_back(
1032        syncer::SyncChange(
1033             FROM_HERE,
1034             syncer::SyncChange::ACTION_UPDATE,
1035             syncer::SyncData::CreateRemoteData(1,
1036                                                tracking_specifics,
1037                                                base::Time())));
1038    tracking_specifics.mutable_favicon_tracking()->set_last_visit_time_ms(i-1);
1039    initial_tracking_data.push_back(
1040        syncer::SyncData::CreateRemoteData(1,
1041                                           tracking_specifics,
1042                                           base::Time()));
1043  }
1044
1045  SetUpInitialSync(initial_image_data, initial_tracking_data);
1046
1047  // Now receive the new icons as an update.
1048  cache()->ProcessSyncChanges(FROM_HERE, new_changes);
1049  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
1050  ASSERT_TRUE(VerifyLocalIcons(expected_icons));
1051}
1052
1053// Receiving the same tracking information as the local data should have no
1054// effect.
1055TEST_F(SyncFaviconCacheTest, ReceiveSameTracking) {
1056  syncer::SyncDataList initial_image_data, initial_tracking_data;
1057  syncer::SyncChangeList same_changes;
1058  std::vector<int> expected_icons;
1059  for (int i = 0; i < kFaviconBatchSize; ++i) {
1060    expected_icons.push_back(i);
1061    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
1062    FillImageSpecifics(BuildFaviconData(i),
1063                       image_specifics.mutable_favicon_image());
1064    initial_image_data.push_back(
1065        syncer::SyncData::CreateRemoteData(1,
1066                                           image_specifics,
1067                                           base::Time()));
1068    FillTrackingSpecifics(BuildFaviconData(i),
1069                          tracking_specifics.mutable_favicon_tracking());
1070    initial_tracking_data.push_back(
1071        syncer::SyncData::CreateRemoteData(1,
1072                                           tracking_specifics,
1073                                           base::Time()));
1074    same_changes.push_back(
1075        syncer::SyncChange(
1076             FROM_HERE,
1077             syncer::SyncChange::ACTION_UPDATE,
1078             syncer::SyncData::CreateRemoteData(1,
1079                                                tracking_specifics,
1080                                                base::Time())));
1081  }
1082
1083  SetUpInitialSync(initial_image_data, initial_tracking_data);
1084
1085  // Now receive the new icons as an update.
1086  cache()->ProcessSyncChanges(FROM_HERE, same_changes);
1087  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
1088  ASSERT_TRUE(VerifyLocalIcons(expected_icons));
1089}
1090
1091// Verify we can delete favicons after setting up sync.
1092TEST_F(SyncFaviconCacheTest, DeleteFavicons) {
1093  syncer::SyncDataList initial_image_data, initial_tracking_data;
1094  syncer::SyncChangeList tracking_deletions, image_deletions;
1095  for (int i = 0; i < kFaviconBatchSize; ++i) {
1096    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
1097    FillImageSpecifics(BuildFaviconData(i),
1098                       image_specifics.mutable_favicon_image());
1099    initial_image_data.push_back(
1100        syncer::SyncData::CreateRemoteData(1,
1101                                           image_specifics,
1102                                           base::Time()));
1103    FillTrackingSpecifics(BuildFaviconData(i),
1104                          tracking_specifics.mutable_favicon_tracking());
1105    initial_tracking_data.push_back(
1106        syncer::SyncData::CreateRemoteData(1,
1107                                           tracking_specifics,
1108                                           base::Time()));
1109    tracking_deletions.push_back(
1110        syncer::SyncChange(
1111             FROM_HERE,
1112             syncer::SyncChange::ACTION_DELETE,
1113             syncer::SyncData::CreateRemoteData(1,
1114                                                tracking_specifics,
1115                                                base::Time())));
1116    image_deletions.push_back(
1117        syncer::SyncChange(
1118             FROM_HERE,
1119             syncer::SyncChange::ACTION_DELETE,
1120             syncer::SyncData::CreateRemoteData(1,
1121                                                image_specifics,
1122                                                base::Time())));
1123  }
1124
1125  SetUpInitialSync(initial_image_data, initial_tracking_data);
1126
1127  // Now receive the tracking deletions. Since we'll still have orphan data,
1128  // the favicon count should remain the same.
1129  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1130  cache()->ProcessSyncChanges(FROM_HERE, tracking_deletions);
1131  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
1132  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1133
1134  // Once the image deletions arrive, the favicon count should be 0 again.
1135  cache()->ProcessSyncChanges(FROM_HERE, image_deletions);
1136  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
1137  EXPECT_EQ(0U, GetFaviconCount());
1138}
1139
1140// Ensure that MergeDataAndStartSyncing enforces the sync favicon limit by
1141// dropping local icons.
1142TEST_F(SyncFaviconCacheTest, ExpireOnMergeData) {
1143  std::vector<int> expected_icons;
1144  syncer::SyncDataList initial_image_data, initial_tracking_data;
1145
1146  // Set up sync so it has the maximum number of favicons, while the local has
1147  // the same amount of different favicons.
1148  for (int i = 0; i < kMaxSyncFavicons; ++i) {
1149    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
1150    FillImageSpecifics(BuildFaviconData(i),
1151                       image_specifics.mutable_favicon_image());
1152    initial_image_data.push_back(
1153        syncer::SyncData::CreateRemoteData(1,
1154                                           image_specifics,
1155                                           base::Time()));
1156    FillTrackingSpecifics(BuildFaviconData(i),
1157                          tracking_specifics.mutable_favicon_tracking());
1158    initial_tracking_data.push_back(
1159        syncer::SyncData::CreateRemoteData(1,
1160                                           tracking_specifics,
1161                                           base::Time()));
1162    expected_icons.push_back(i);
1163
1164    TestFaviconData favicon = BuildFaviconData(i+kMaxSyncFavicons);
1165    TriggerSyncFaviconReceived(favicon.page_url,
1166                               favicon.icon_url,
1167                               favicon.image_16,
1168                               i+kMaxSyncFavicons);
1169  }
1170
1171  EXPECT_FALSE(VerifyLocalIcons(expected_icons));
1172
1173  syncer::SyncMergeResult merge_result =
1174      cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
1175                                        initial_image_data,
1176                                        CreateAndPassProcessor(),
1177                                        CreateAndPassSyncErrorFactory());
1178  EXPECT_EQ((unsigned long)kMaxSyncFavicons,
1179            cache()->GetAllSyncData(syncer::FAVICON_IMAGES).size());
1180  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
1181  EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_added());
1182  EXPECT_EQ(0, merge_result.num_items_modified());
1183  EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_deleted());
1184  EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_before_association());
1185  EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_after_association());
1186
1187  merge_result =
1188      cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
1189                                        initial_tracking_data,
1190                                        CreateAndPassProcessor(),
1191                                        CreateAndPassSyncErrorFactory());
1192  EXPECT_EQ((unsigned long)kMaxSyncFavicons,
1193            cache()->GetAllSyncData(syncer::FAVICON_TRACKING).size());
1194  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
1195  EXPECT_EQ(0, merge_result.num_items_added());
1196  EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_modified());
1197  EXPECT_EQ(0, merge_result.num_items_deleted());
1198  EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_before_association());
1199  EXPECT_EQ(kMaxSyncFavicons, merge_result.num_items_after_association());
1200
1201  EXPECT_TRUE(VerifyLocalIcons(expected_icons));
1202}
1203
1204// Receiving sync additions (via ProcessSyncChanges) should not trigger
1205// expirations.
1206TEST_F(SyncFaviconCacheTest, NoExpireOnProcessSyncChanges) {
1207  syncer::SyncDataList initial_image_data, initial_tracking_data;
1208  syncer::SyncChangeList image_changes, tracking_changes;
1209  std::vector<int> expected_icons;
1210  for (int i = 0; i < kMaxSyncFavicons; ++i) {
1211    expected_icons.push_back(i);
1212    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
1213    FillImageSpecifics(BuildFaviconData(i),
1214                       image_specifics.mutable_favicon_image());
1215    initial_image_data.push_back(
1216        syncer::SyncData::CreateRemoteData(1,
1217                                           image_specifics,
1218                                           base::Time()));
1219    FillTrackingSpecifics(BuildFaviconData(i),
1220                          tracking_specifics.mutable_favicon_tracking());
1221    initial_tracking_data.push_back(
1222        syncer::SyncData::CreateRemoteData(1,
1223                                           tracking_specifics,
1224                                           base::Time()));
1225    // Set up new tracking specifics for the icons received at change time.
1226    expected_icons.push_back(i + kMaxSyncFavicons);
1227    FillImageSpecifics(BuildFaviconData(i + kMaxSyncFavicons),
1228                       image_specifics.mutable_favicon_image());
1229    image_changes.push_back(
1230        syncer::SyncChange(
1231             FROM_HERE,
1232             syncer::SyncChange::ACTION_ADD,
1233             syncer::SyncData::CreateRemoteData(1,
1234                                                image_specifics,
1235                                                base::Time())));
1236    FillTrackingSpecifics(BuildFaviconData(i + kMaxSyncFavicons),
1237                          tracking_specifics.mutable_favicon_tracking());
1238    tracking_changes.push_back(
1239        syncer::SyncChange(
1240             FROM_HERE,
1241             syncer::SyncChange::ACTION_ADD,
1242             syncer::SyncData::CreateRemoteData(1,
1243                                                tracking_specifics,
1244                                                base::Time())));
1245  }
1246
1247  SetUpInitialSync(initial_image_data, initial_tracking_data);
1248
1249  // Now receive the new icons as an update.
1250  EXPECT_EQ((unsigned long)kMaxSyncFavicons, GetFaviconCount());
1251  cache()->ProcessSyncChanges(FROM_HERE, image_changes);
1252  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
1253  cache()->ProcessSyncChanges(FROM_HERE, tracking_changes);
1254  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
1255  EXPECT_TRUE(VerifyLocalIcons(expected_icons));
1256  EXPECT_GT(GetFaviconCount(), (unsigned long)kMaxSyncFavicons);
1257}
1258
1259// Test that visiting a new page triggers a favicon load and a sync addition.
1260TEST_F(SyncFaviconCacheTest, AddOnFaviconVisited) {
1261  EXPECT_EQ(0U, GetFaviconCount());
1262  SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList());
1263  std::vector<int> expected_icons;
1264
1265  for (int i = 0; i < kFaviconBatchSize; ++i) {
1266    expected_icons.push_back(i);
1267    TestFaviconData test_data = BuildFaviconData(i);
1268    cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url);
1269  }
1270
1271  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetTaskCount());
1272
1273  for (int i = 0; i < kFaviconBatchSize; ++i) {
1274    TestFaviconData test_data = BuildFaviconData(i);
1275    OnCustomFaviconDataAvailable(test_data);
1276
1277    syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1278    ASSERT_EQ(2U, changes.size());
1279    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
1280    EXPECT_EQ(syncer::FAVICON_IMAGES, changes[0].sync_data().GetDataType());
1281    EXPECT_TRUE(
1282        CompareFaviconDataToSpecifics(test_data,
1283                                      changes[0].sync_data().GetSpecifics()));
1284    EXPECT_EQ(syncer::FAVICON_TRACKING, changes[1].sync_data().GetDataType());
1285    // Just verify the favicon url for the tracking specifics and that the
1286    // timestamp is non-null.
1287    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[1].change_type());
1288    EXPECT_EQ(test_data.icon_url.spec(),
1289              changes[1].sync_data().GetSpecifics().favicon_tracking().
1290                  favicon_url());
1291    EXPECT_NE(changes[1].sync_data().GetSpecifics().favicon_tracking().
1292                  last_visit_time_ms(), 0);
1293  }
1294
1295  EXPECT_EQ(0U, GetTaskCount());
1296  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1297}
1298
1299// Test that visiting a known page does not trigger a favicon load and just
1300// updates the sync tracking info.
1301TEST_F(SyncFaviconCacheTest, UpdateOnFaviconVisited) {
1302  EXPECT_EQ(0U, GetFaviconCount());
1303  SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList());
1304  std::vector<int> expected_icons;
1305
1306  // Add the favicons.
1307  for (int i = 0; i < kFaviconBatchSize; ++i) {
1308    expected_icons.push_back(i);
1309    TestFaviconData test_data = BuildFaviconData(i);
1310    cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url);
1311    OnCustomFaviconDataAvailable(test_data);
1312  }
1313  syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1314
1315  // Visit the favicons again.
1316  EXPECT_EQ(0U, GetTaskCount());
1317  for (int i = 0; i < kFaviconBatchSize; ++i) {
1318    TestFaviconData test_data = BuildFaviconData(i);
1319    cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url);
1320
1321    syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1322    ASSERT_EQ(1U, changes.size());
1323    // Just verify the favicon url for the tracking specifics and that the
1324    // timestamp is non-null.
1325    EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
1326    EXPECT_EQ(test_data.icon_url.spec(),
1327              changes[0].sync_data().GetSpecifics().favicon_tracking().
1328                  favicon_url());
1329    EXPECT_NE(changes[0].sync_data().GetSpecifics().favicon_tracking().
1330                  last_visit_time_ms(), 0);
1331  }
1332  EXPECT_EQ(0U, GetTaskCount());
1333  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1334}
1335
1336// Ensure we properly expire old synced favicons as new ones are updated.
1337TEST_F(SyncFaviconCacheTest, ExpireOnFaviconVisited) {
1338  EXPECT_EQ(0U, GetFaviconCount());
1339  SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList());
1340  std::vector<int> expected_icons;
1341
1342  // Add the initial favicons.
1343  for (int i = 0; i < kMaxSyncFavicons; ++i) {
1344    expected_icons.push_back(i);
1345    TestFaviconData test_data = BuildFaviconData(i);
1346    cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url);
1347    OnCustomFaviconDataAvailable(test_data);
1348  }
1349  syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1350
1351  // Visit some new favicons, triggering expirations of the old favicons.
1352  EXPECT_EQ(0U, GetTaskCount());
1353  for (int i = 0; i < kFaviconBatchSize; ++i) {
1354    TestFaviconData old_favicon = BuildFaviconData(i);
1355    TestFaviconData test_data = BuildFaviconData(i + kMaxSyncFavicons);
1356    cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url);
1357    OnCustomFaviconDataAvailable(test_data);
1358
1359    syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1360    ASSERT_EQ(4U, changes.size());
1361    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
1362    EXPECT_TRUE(
1363        CompareFaviconDataToSpecifics(test_data,
1364                                      changes[0].sync_data().GetSpecifics()));
1365    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, changes[1].change_type());
1366    EXPECT_EQ(old_favicon.icon_url.spec(), changes[1].sync_data().GetTag());
1367
1368    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[2].change_type());
1369    EXPECT_EQ(test_data.icon_url.spec(),
1370              changes[2].sync_data().GetSpecifics().favicon_tracking().
1371                  favicon_url());
1372    EXPECT_NE(changes[2].sync_data().GetSpecifics().favicon_tracking().
1373                  last_visit_time_ms(), 0);
1374    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, changes[3].change_type());
1375    EXPECT_EQ(old_favicon.icon_url.spec(), changes[3].sync_data().GetTag());
1376  }
1377
1378  EXPECT_EQ(0U, GetTaskCount());
1379  EXPECT_EQ((unsigned long)kMaxSyncFavicons, GetFaviconCount());
1380}
1381
1382// A full history clear notification should result in all synced favicons being
1383// deleted.
1384TEST_F(SyncFaviconCacheTest, HistoryFullClear) {
1385  syncer::SyncDataList initial_image_data, initial_tracking_data;
1386  std::vector<int> expected_icons;
1387  std::vector<syncer::SyncChange::SyncChangeType> expected_deletions;
1388  for (int i = 0; i < kFaviconBatchSize; ++i) {
1389    expected_icons.push_back(i);
1390    expected_deletions.push_back(syncer::SyncChange::ACTION_DELETE);
1391    TestFaviconData test_data = BuildFaviconData(i);
1392    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
1393    FillImageSpecifics(test_data,
1394                       image_specifics.mutable_favicon_image());
1395    initial_image_data.push_back(
1396        syncer::SyncData::CreateRemoteData(1,
1397                                           image_specifics,
1398                                           base::Time()));
1399    FillTrackingSpecifics(BuildFaviconData(i),
1400                          tracking_specifics.mutable_favicon_tracking());
1401    initial_tracking_data.push_back(
1402        syncer::SyncData::CreateRemoteData(1,
1403                                           tracking_specifics,
1404                                           base::Time()));
1405  }
1406
1407  SetUpInitialSync(initial_image_data, initial_tracking_data);
1408  syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1409  EXPECT_TRUE(changes.empty());
1410
1411  history::URLsDeletedDetails deletions;
1412  deletions.all_history = true;
1413  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1414  content::NotificationService::current()->Notify(
1415        chrome::NOTIFICATION_HISTORY_URLS_DELETED,
1416        content::Source<Profile>(NULL),
1417        content::Details<history::URLsDeletedDetails>(&deletions));
1418  EXPECT_EQ(0U, GetFaviconCount());
1419  changes = processor()->GetAndResetChangeList();
1420  ASSERT_EQ(changes.size(), (unsigned long)kFaviconBatchSize*2);
1421  syncer::SyncChangeList changes_1, changes_2;
1422  for (int i = 0; i < kFaviconBatchSize; ++i) {
1423    changes_1.push_back(changes[i]);
1424    changes_2.push_back(changes[i + kFaviconBatchSize]);
1425  }
1426  VerifyChanges(syncer::FAVICON_IMAGES,
1427                expected_deletions,
1428                expected_icons,
1429                changes_1);
1430  VerifyChanges(syncer::FAVICON_TRACKING,
1431                expected_deletions,
1432                expected_icons,
1433                changes_2);
1434}
1435
1436// A partial history clear notification should result in the expired favicons
1437// also being deleted from sync.
1438TEST_F(SyncFaviconCacheTest, HistorySubsetClear) {
1439  syncer::SyncDataList initial_image_data, initial_tracking_data;
1440  std::vector<int> expected_icons;
1441  std::vector<syncer::SyncChange::SyncChangeType> expected_deletions;
1442  history::URLsDeletedDetails deletions;
1443  for (int i = 0; i < kFaviconBatchSize; ++i) {
1444    TestFaviconData test_data = BuildFaviconData(i);
1445    if (i < kFaviconBatchSize/2) {
1446      expected_icons.push_back(i);
1447      expected_deletions.push_back(syncer::SyncChange::ACTION_DELETE);
1448      deletions.favicon_urls.insert(test_data.icon_url);
1449    }
1450    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
1451    FillImageSpecifics(test_data,
1452                       image_specifics.mutable_favicon_image());
1453    initial_image_data.push_back(
1454        syncer::SyncData::CreateRemoteData(1,
1455                                           image_specifics,
1456                                           base::Time()));
1457    FillTrackingSpecifics(BuildFaviconData(i),
1458                          tracking_specifics.mutable_favicon_tracking());
1459    initial_tracking_data.push_back(
1460        syncer::SyncData::CreateRemoteData(1,
1461                                           tracking_specifics,
1462                                           base::Time()));
1463  }
1464
1465  SetUpInitialSync(initial_image_data, initial_tracking_data);
1466  syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1467  EXPECT_TRUE(changes.empty());
1468
1469  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1470  content::NotificationService::current()->Notify(
1471        chrome::NOTIFICATION_HISTORY_URLS_DELETED,
1472        content::Source<Profile>(NULL),
1473        content::Details<history::URLsDeletedDetails>(&deletions));
1474  EXPECT_EQ((unsigned long)kFaviconBatchSize/2, GetFaviconCount());
1475  changes = processor()->GetAndResetChangeList();
1476  ASSERT_EQ(changes.size(), (unsigned long)kFaviconBatchSize);
1477  syncer::SyncChangeList changes_1, changes_2;
1478  for (size_t i = 0; i < kFaviconBatchSize/2; ++i) {
1479    changes_1.push_back(changes[i]);
1480    changes_2.push_back(changes[i + kFaviconBatchSize/2]);
1481  }
1482  VerifyChanges(syncer::FAVICON_IMAGES,
1483                expected_deletions,
1484                expected_icons,
1485                changes_1);
1486  VerifyChanges(syncer::FAVICON_TRACKING,
1487                expected_deletions,
1488                expected_icons,
1489                changes_2);
1490}
1491
1492// Any favicon urls with the "data" scheme should be ignored.
1493TEST_F(SyncFaviconCacheTest, IgnoreDataScheme) {
1494  EXPECT_EQ(0U, GetFaviconCount());
1495  SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList());
1496  std::vector<int> expected_icons;
1497
1498  for (int i = 0; i < kFaviconBatchSize; ++i) {
1499    TestFaviconData test_data = BuildFaviconData(i);
1500    cache()->OnFaviconVisited(test_data.page_url, GURL());
1501  }
1502
1503  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetTaskCount());
1504
1505  for (int i = 0; i < kFaviconBatchSize; ++i) {
1506    TestFaviconData test_data = BuildFaviconData(i);
1507    test_data.icon_url = GURL("data:image/png;base64;blabla");
1508    EXPECT_TRUE(test_data.icon_url.is_valid());
1509    OnCustomFaviconDataAvailable(test_data);
1510  }
1511
1512  EXPECT_EQ(0U, GetTaskCount());
1513  EXPECT_EQ(0U, GetFaviconCount());
1514  syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1515  EXPECT_TRUE(changes.empty());
1516}
1517
1518// When visiting a page we've already loaded the favicon for, don't attempt to
1519// reload the favicon, just update the visit time using the cached icon url.
1520TEST_F(SyncFaviconCacheTest, ReuseCachedIconUrl) {
1521  EXPECT_EQ(0U, GetFaviconCount());
1522  SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList());
1523  std::vector<int> expected_icons;
1524
1525  for (int i = 0; i < kFaviconBatchSize; ++i) {
1526    expected_icons.push_back(i);
1527    TestFaviconData test_data = BuildFaviconData(i);
1528    cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url);
1529  }
1530
1531  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetTaskCount());
1532
1533  for (int i = 0; i < kFaviconBatchSize; ++i) {
1534    TestFaviconData test_data = BuildFaviconData(i);
1535    OnCustomFaviconDataAvailable(test_data);
1536  }
1537  processor()->GetAndResetChangeList();
1538  EXPECT_EQ(0U, GetTaskCount());
1539  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1540
1541  for (int i = 0; i < kFaviconBatchSize; ++i) {
1542    TestFaviconData test_data = BuildFaviconData(i);
1543    cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url);
1544    syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1545    ASSERT_EQ(1U, changes.size());
1546    // Just verify the favicon url for the tracking specifics and that the
1547    // timestamp is non-null.
1548    EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
1549    EXPECT_EQ(test_data.icon_url.spec(),
1550              changes[0].sync_data().GetSpecifics().favicon_tracking().
1551                  favicon_url());
1552    EXPECT_NE(changes[0].sync_data().GetSpecifics().favicon_tracking().
1553                  last_visit_time_ms(), 0);
1554  }
1555  EXPECT_EQ(0U, GetTaskCount());
1556}
1557
1558// If we wind up with orphan image/tracking nodes, then receive an update
1559// for those favicons, we should lazily create the missing nodes.
1560TEST_F(SyncFaviconCacheTest, UpdatedOrphans) {
1561  EXPECT_EQ(0U, GetFaviconCount());
1562  SetUpInitialSync(syncer::SyncDataList(), syncer::SyncDataList());
1563
1564  syncer::SyncChangeList initial_image_changes;
1565  syncer::SyncChangeList initial_tracking_changes;
1566  for (int i = 0; i < kFaviconBatchSize; ++i) {
1567    TestFaviconData test_data = BuildFaviconData(i);
1568    // Even favicons have image data but no tracking data. Odd favicons have
1569    // tracking data but no image data.
1570    if (i % 2 == 0) {
1571      sync_pb::EntitySpecifics image_specifics;
1572      FillImageSpecifics(BuildFaviconData(i),
1573                         image_specifics.mutable_favicon_image());
1574      initial_image_changes.push_back(
1575          syncer::SyncChange(FROM_HERE,
1576                             syncer::SyncChange::ACTION_ADD,
1577                             syncer::SyncData::CreateRemoteData(
1578                                 1, image_specifics, base::Time())));
1579    } else {
1580      sync_pb::EntitySpecifics tracking_specifics;
1581      FillTrackingSpecifics(BuildFaviconData(i),
1582                            tracking_specifics.mutable_favicon_tracking());
1583      initial_tracking_changes.push_back(
1584          syncer::SyncChange(FROM_HERE,
1585                             syncer::SyncChange::ACTION_ADD,
1586                             syncer::SyncData::CreateRemoteData(
1587                                 1, tracking_specifics, base::Time())));
1588    }
1589  }
1590
1591  cache()->ProcessSyncChanges(FROM_HERE, initial_image_changes);
1592  cache()->ProcessSyncChanges(FROM_HERE, initial_tracking_changes);
1593  EXPECT_EQ(0U, processor()->GetAndResetChangeList().size());
1594  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1595
1596  for (int i = 0; i < kFaviconBatchSize/2; ++i) {
1597    TestFaviconData test_data = BuildFaviconData(i);
1598    cache()->OnFaviconVisited(test_data.page_url, GURL());
1599    EXPECT_EQ(1U, GetTaskCount());
1600    OnCustomFaviconDataAvailable(test_data);
1601    syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1602
1603    // Even favicons had image data, so should now receive new tracking data
1604    // and updated image data (we allow one update after the initial add).
1605    // Odd favicons had tracking so should now receive new image data and
1606    // updated tracking data.
1607    if (i % 2 == 0) {
1608      ASSERT_EQ(2U, changes.size());
1609      EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
1610      EXPECT_TRUE(
1611          CompareFaviconDataToSpecifics(test_data,
1612                                        changes[0].sync_data().GetSpecifics()));
1613      EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[1].change_type());
1614      EXPECT_EQ(test_data.icon_url.spec(),
1615                changes[1].sync_data().GetSpecifics().favicon_tracking().
1616                    favicon_url());
1617      EXPECT_NE(changes[1].sync_data().GetSpecifics().favicon_tracking().
1618                    last_visit_time_ms(), 0);
1619    } else {
1620      ASSERT_EQ(2U, changes.size());
1621      EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
1622      EXPECT_TRUE(
1623          CompareFaviconDataToSpecifics(test_data,
1624                                        changes[0].sync_data().GetSpecifics()));
1625      EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[1].change_type());
1626      EXPECT_EQ(test_data.icon_url.spec(),
1627                changes[1].sync_data().GetSpecifics().favicon_tracking().
1628                    favicon_url());
1629      EXPECT_NE(changes[1].sync_data().GetSpecifics().favicon_tracking().
1630                    last_visit_time_ms(), 0);
1631    }
1632  }
1633
1634  EXPECT_EQ(0U, GetTaskCount());
1635  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1636}
1637
1638// Verify that orphaned favicon images don't result in creating invalid
1639// favicon tracking data.
1640TEST_F(SyncFaviconCacheTest, PartialAssociationInfo) {
1641  syncer::SyncDataList initial_image_data, initial_tracking_data;
1642  for (int i = 0; i < kFaviconBatchSize; ++i) {
1643    sync_pb::EntitySpecifics image_specifics;
1644    FillImageSpecifics(BuildFaviconData(i),
1645                       image_specifics.mutable_favicon_image());
1646    initial_image_data.push_back(
1647        syncer::SyncData::CreateRemoteData(1,
1648                                           image_specifics,
1649                                           base::Time()));
1650    image_specifics.mutable_favicon_image()->clear_favicon_web();
1651  }
1652
1653  SetUpInitialSync(initial_image_data, initial_tracking_data);
1654  syncer::SyncChangeList change_list = processor()->GetAndResetChangeList();
1655  EXPECT_TRUE(change_list.empty());
1656  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1657}
1658
1659// Tests that we don't choke if a favicon visit node with a null visit time is
1660// present (see crbug.com/258196) and an update is made.
1661TEST_F(SyncFaviconCacheTest, NullFaviconVisitTime) {
1662  EXPECT_EQ(0U, GetFaviconCount());
1663
1664  syncer::SyncDataList initial_image_data, initial_tracking_data;
1665  std::vector<int> expected_icons;
1666  for (int i = 0; i < kFaviconBatchSize; ++i) {
1667    expected_icons.push_back(i);
1668    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
1669    FillImageSpecifics(BuildFaviconData(i),
1670                       image_specifics.mutable_favicon_image());
1671    initial_image_data.push_back(
1672        syncer::SyncData::CreateRemoteData(1,
1673                                           image_specifics,
1674                                           base::Time()));
1675    FillTrackingSpecifics(BuildFaviconData(i),
1676                          tracking_specifics.mutable_favicon_tracking());
1677    tracking_specifics.mutable_favicon_tracking()->set_last_visit_time_ms(
1678        syncer::TimeToProtoTime(base::Time()));
1679    initial_tracking_data.push_back(
1680        syncer::SyncData::CreateRemoteData(1,
1681                                           tracking_specifics,
1682                                           base::Time()));
1683  }
1684
1685  cache()->MergeDataAndStartSyncing(syncer::FAVICON_IMAGES,
1686                                    initial_image_data,
1687                                    CreateAndPassProcessor(),
1688                                    CreateAndPassSyncErrorFactory());
1689  ASSERT_EQ(0U, processor()->GetAndResetChangeList().size());
1690  cache()->MergeDataAndStartSyncing(syncer::FAVICON_TRACKING,
1691                                    initial_tracking_data,
1692                                    CreateAndPassProcessor(),
1693                                    CreateAndPassSyncErrorFactory());
1694  ASSERT_EQ((unsigned long)kFaviconBatchSize,
1695            processor()->GetAndResetChangeList().size());
1696  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1697
1698  // Visit the favicons again.
1699  EXPECT_EQ(0U, GetTaskCount());
1700  for (int i = 0; i < kFaviconBatchSize; ++i) {
1701    TestFaviconData test_data = BuildFaviconData(i);
1702    cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url);
1703
1704    syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1705    ASSERT_EQ(1U, changes.size());
1706    // Just verify the favicon url for the tracking specifics and that the
1707    // timestamp is non-null.
1708    EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
1709    EXPECT_EQ(test_data.icon_url.spec(),
1710              changes[0].sync_data().GetSpecifics().favicon_tracking().
1711                  favicon_url());
1712    EXPECT_NE(changes[0].sync_data().GetSpecifics().favicon_tracking().
1713                  last_visit_time_ms(), 0);
1714  }
1715  EXPECT_EQ(0U, GetTaskCount());
1716  EXPECT_EQ((unsigned long)kFaviconBatchSize, GetFaviconCount());
1717}
1718
1719// If another synced client has a clock skewed towards the future, it's possible
1720// that favicons added locally will be expired as they are added. Ensure this
1721// doesn't crash (see crbug.com/306150).
1722TEST_F(SyncFaviconCacheTest, VisitFaviconClockSkew) {
1723  EXPECT_EQ(0U, GetFaviconCount());
1724  const int kClockSkew = 20;  // 20 minutes in the future.
1725
1726  // Set up sync with kMaxSyncFavicons starting kClockSkew minutes in the
1727  // future.
1728  syncer::SyncDataList initial_image_data, initial_tracking_data;
1729  for (int i = 0; i < kMaxSyncFavicons; ++i) {
1730    sync_pb::EntitySpecifics image_specifics, tracking_specifics;
1731    TestFaviconData test_data = BuildFaviconData(i);
1732    test_data.last_visit_time =
1733        syncer::TimeToProtoTime(
1734            base::Time::Now() + base::TimeDelta::FromMinutes(kClockSkew));
1735    FillImageSpecifics(test_data,
1736                       image_specifics.mutable_favicon_image());
1737    initial_image_data.push_back(
1738        syncer::SyncData::CreateRemoteData(1,
1739                                           image_specifics,
1740                                           base::Time()));
1741    FillTrackingSpecifics(test_data,
1742                          tracking_specifics.mutable_favicon_tracking());
1743    initial_tracking_data.push_back(
1744        syncer::SyncData::CreateRemoteData(1,
1745                                           tracking_specifics,
1746                                           base::Time()));
1747  }
1748  SetUpInitialSync(initial_image_data, initial_tracking_data);
1749
1750  // Visit some new favicons with local time, which will be expired as they
1751  // are added.
1752  EXPECT_EQ(0U, GetTaskCount());
1753  for (int i = 0; i < kClockSkew; ++i) {
1754    TestFaviconData test_data = BuildFaviconData(i + kMaxSyncFavicons);
1755    cache()->OnFaviconVisited(test_data.page_url, test_data.icon_url);
1756    OnCustomFaviconDataAvailable(test_data);
1757
1758    // The changes will be an add followed by a delete for both the image and
1759    // tracking info.
1760    syncer::SyncChangeList changes = processor()->GetAndResetChangeList();
1761    ASSERT_EQ(changes.size(), 4U);
1762    ASSERT_EQ(changes[0].change_type(), syncer::SyncChange::ACTION_ADD);
1763    ASSERT_EQ(changes[0].sync_data().GetDataType(), syncer::FAVICON_IMAGES);
1764    ASSERT_EQ(changes[1].change_type(), syncer::SyncChange::ACTION_DELETE);
1765    ASSERT_EQ(changes[1].sync_data().GetDataType(), syncer::FAVICON_IMAGES);
1766    ASSERT_EQ(changes[2].change_type(), syncer::SyncChange::ACTION_ADD);
1767    ASSERT_EQ(changes[2].sync_data().GetDataType(), syncer::FAVICON_TRACKING);
1768    ASSERT_EQ(changes[3].change_type(), syncer::SyncChange::ACTION_DELETE);
1769    ASSERT_EQ(changes[3].sync_data().GetDataType(), syncer::FAVICON_TRACKING);
1770  }
1771  EXPECT_EQ(0U, GetTaskCount());
1772  EXPECT_EQ((unsigned long)kMaxSyncFavicons, GetFaviconCount());
1773}
1774
1775}  // namespace browser_sync
1776