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