- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync / profile_sync_service_typed_url_unittest.cc
1 // Copyright (c) 2012 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 <vector>
6
7 #include "testing/gtest/include/gtest/gtest.h"
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/location.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/thread.h"
17 #include "base/time/time.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/history/history_backend.h"
20 #include "chrome/browser/history/history_db_task.h"
21 #include "chrome/browser/history/history_notifications.h"
22 #include "chrome/browser/history/history_service.h"
23 #include "chrome/browser/history/history_service_factory.h"
24 #include "chrome/browser/history/history_types.h"
25 #include "chrome/browser/invalidation/invalidation_service_factory.h"
26 #include "chrome/browser/signin/signin_manager.h"
27 #include "chrome/browser/signin/signin_manager_factory.h"
28 #include "chrome/browser/signin/token_service_factory.h"
29 #include "chrome/browser/sync/abstract_profile_sync_service_test.h"
30 #include "chrome/browser/sync/glue/data_type_error_handler_mock.h"
31 #include "chrome/browser/sync/glue/sync_backend_host.h"
32 #include "chrome/browser/sync/glue/typed_url_change_processor.h"
33 #include "chrome/browser/sync/glue/typed_url_data_type_controller.h"
34 #include "chrome/browser/sync/glue/typed_url_model_associator.h"
35 #include "chrome/browser/sync/profile_sync_components_factory.h"
36 #include "chrome/browser/sync/profile_sync_components_factory_mock.h"
37 #include "chrome/browser/sync/profile_sync_service.h"
38 #include "chrome/browser/sync/profile_sync_service_factory.h"
39 #include "chrome/browser/sync/profile_sync_test_util.h"
40 #include "chrome/browser/sync/test_profile_sync_service.h"
41 #include "chrome/test/base/testing_profile.h"
42 #include "components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h"
43 #include "content/public/browser/notification_service.h"
44 #include "google_apis/gaia/gaia_constants.h"
45 #include "sync/internal_api/public/read_node.h"
46 #include "sync/internal_api/public/read_transaction.h"
47 #include "sync/internal_api/public/write_node.h"
48 #include "sync/internal_api/public/write_transaction.h"
49 #include "sync/protocol/typed_url_specifics.pb.h"
50 #include "testing/gmock/include/gmock/gmock.h"
51 #include "url/gurl.h"
52
53 using base::Time;
54 using base::Thread;
55 using browser_sync::TypedUrlChangeProcessor;
56 using browser_sync::TypedUrlDataTypeController;
57 using browser_sync::TypedUrlModelAssociator;
58 using history::HistoryBackend;
59 using history::URLID;
60 using history::URLRow;
61 using syncer::syncable::WriteTransaction;
62 using testing::_;
63 using testing::DoAll;
64 using testing::Return;
65 using testing::SetArgumentPointee;
66
67 namespace {
68 // Visits with this timestamp are treated as expired.
69 static const int EXPIRED_VISIT = -1;
70
71 class HistoryBackendMock : public HistoryBackend {
72  public:
73   HistoryBackendMock() : HistoryBackend(base::FilePath(), 0, NULL, NULL) {}
74   virtual bool IsExpiredVisitTime(const base::Time& time) OVERRIDE {
75     return time.ToInternalValue() == EXPIRED_VISIT;
76   }
77   MOCK_METHOD1(GetAllTypedURLs, bool(history::URLRows* entries));
78   MOCK_METHOD3(GetMostRecentVisitsForURL, bool(history::URLID id,
79                                                int max_visits,
80                                                history::VisitVector* visits));
81   MOCK_METHOD2(UpdateURL, bool(history::URLID id, const history::URLRow& url));
82   MOCK_METHOD3(AddVisits, bool(const GURL& url,
83                                const std::vector<history::VisitInfo>& visits,
84                                history::VisitSource visit_source));
85   MOCK_METHOD1(RemoveVisits, bool(const history::VisitVector& visits));
86   MOCK_METHOD2(GetURL, bool(const GURL& url_id, history::URLRow* url_row));
87   MOCK_METHOD2(SetPageTitle, void(const GURL& url, const string16& title));
88   MOCK_METHOD1(DeleteURL, void(const GURL& url));
89
90  private:
91   virtual ~HistoryBackendMock() {}
92 };
93
94 class HistoryServiceMock : public HistoryService {
95  public:
96   explicit HistoryServiceMock(Profile* profile) : HistoryService(profile) {}
97   MOCK_METHOD2(ScheduleDBTask, void(history::HistoryDBTask*,
98                                     CancelableRequestConsumerBase*));
99   MOCK_METHOD0(Shutdown, void());
100
101   void ShutdownBaseService() {
102     HistoryService::Shutdown();
103   }
104
105  private:
106   virtual ~HistoryServiceMock() {}
107 };
108
109 BrowserContextKeyedService* BuildHistoryService(
110     content::BrowserContext* profile) {
111   return new HistoryServiceMock(static_cast<Profile*>(profile));
112 }
113
114 class TestTypedUrlModelAssociator : public TypedUrlModelAssociator {
115  public:
116   TestTypedUrlModelAssociator(
117       ProfileSyncService* sync_service,
118       history::HistoryBackend* history_backend,
119       browser_sync::DataTypeErrorHandler* error_handler) :
120       TypedUrlModelAssociator(sync_service, history_backend, error_handler) {}
121
122  protected:
123   // Don't clear error stats - that way we can verify their values in our
124   // tests.
125   virtual void ClearErrorStats() OVERRIDE {}
126 };
127
128 void RunOnDBThreadCallback(HistoryBackend* backend,
129                            history::HistoryDBTask* task) {
130   task->RunOnDBThread(backend, NULL);
131 }
132
133 ACTION_P2(RunTaskOnDBThread, thread, backend) {
134   // ScheduleDBTask takes ownership of its task argument, so we
135   // should, too.
136   scoped_refptr<history::HistoryDBTask> task(arg0);
137   thread->message_loop()->PostTask(
138       FROM_HERE, base::Bind(&RunOnDBThreadCallback, base::Unretained(backend),
139                             task));
140 }
141
142 ACTION_P2(ShutdownHistoryService, thread, service) {
143   service->ShutdownBaseService();
144   delete thread;
145 }
146
147 ACTION_P6(MakeTypedUrlSyncComponents,
148               profile,
149               service,
150               hb,
151               dtc,
152               error_handler,
153               model_associator) {
154   *model_associator =
155       new TestTypedUrlModelAssociator(service, hb, error_handler);
156   TypedUrlChangeProcessor* change_processor =
157       new TypedUrlChangeProcessor(profile, *model_associator, hb, dtc);
158   return ProfileSyncComponentsFactory::SyncComponents(*model_associator,
159                                                       change_processor);
160 }
161
162 class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest {
163  public:
164   void AddTypedUrlSyncNode(const history::URLRow& url,
165                            const history::VisitVector& visits) {
166     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
167     syncer::ReadNode typed_url_root(&trans);
168     ASSERT_EQ(syncer::BaseNode::INIT_OK,
169               typed_url_root.InitByTagLookup(browser_sync::kTypedUrlTag));
170
171     syncer::WriteNode node(&trans);
172     std::string tag = url.url().spec();
173     syncer::WriteNode::InitUniqueByCreationResult result =
174         node.InitUniqueByCreation(syncer::TYPED_URLS, typed_url_root, tag);
175     ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result);
176     TypedUrlModelAssociator::WriteToSyncNode(url, visits, &node);
177   }
178
179  protected:
180   ProfileSyncServiceTypedUrlTest() {
181     history_thread_.reset(new Thread("history"));
182   }
183
184   virtual void SetUp() {
185     AbstractProfileSyncServiceTest::SetUp();
186     TestingProfile::Builder builder;
187     builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(),
188                               FakeOAuth2TokenService::BuildTokenService);
189     profile_ = builder.Build().Pass();
190     invalidation::InvalidationServiceFactory::GetInstance()->
191         SetBuildOnlyFakeInvalidatorsForTest(true);
192     history_backend_ = new HistoryBackendMock();
193     history_service_ = static_cast<HistoryServiceMock*>(
194         HistoryServiceFactory::GetInstance()->SetTestingFactoryAndUse(
195             profile_.get(), BuildHistoryService));
196     EXPECT_CALL((*history_service_), ScheduleDBTask(_, _))
197         .WillRepeatedly(RunTaskOnDBThread(history_thread_.get(),
198                                           history_backend_.get()));
199     history_thread_->Start();
200   }
201
202   virtual void TearDown() {
203     EXPECT_CALL((*history_service_), Shutdown())
204         .WillOnce(ShutdownHistoryService(history_thread_.release(),
205                                          history_service_));
206     profile_.reset();
207     AbstractProfileSyncServiceTest::TearDown();
208   }
209
210   TypedUrlModelAssociator* StartSyncService(const base::Closure& callback) {
211     TypedUrlModelAssociator* model_associator = NULL;
212     if (!sync_service_) {
213       SigninManagerBase* signin =
214           SigninManagerFactory::GetForProfile(profile_.get());
215       signin->SetAuthenticatedUsername("test");
216       token_service_ = static_cast<TokenService*>(
217           TokenServiceFactory::GetInstance()->SetTestingFactoryAndUse(
218               profile_.get(), BuildTokenService));
219       sync_service_ = static_cast<TestProfileSyncService*>(
220           ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
221               profile_.get(),
222               &TestProfileSyncService::BuildAutoStartAsyncInit));
223       sync_service_->set_backend_init_callback(callback);
224       ProfileSyncComponentsFactoryMock* components =
225           sync_service_->components_factory_mock();
226       TypedUrlDataTypeController* data_type_controller =
227           new TypedUrlDataTypeController(components,
228                                          profile_.get(),
229                                          sync_service_);
230
231       EXPECT_CALL(*components, CreateTypedUrlSyncComponents(_, _, _)).
232           WillOnce(MakeTypedUrlSyncComponents(profile_.get(),
233                                               sync_service_,
234                                               history_backend_.get(),
235                                               data_type_controller,
236                                               &error_handler_,
237                                               &model_associator));
238       EXPECT_CALL(*components, CreateDataTypeManager(_, _, _, _, _, _)).
239           WillOnce(ReturnNewDataTypeManager());
240
241       ProfileOAuth2TokenService* oauth2_token_service =
242           ProfileOAuth2TokenServiceFactory::GetForProfile(profile_.get());
243       oauth2_token_service->UpdateCredentials("test", "oauth2_login_token");
244
245       sync_service_->RegisterDataTypeController(data_type_controller);
246
247       sync_service_->Initialize();
248       base::MessageLoop::current()->Run();
249     }
250     return model_associator;
251   }
252
253   void GetTypedUrlsFromSyncDB(history::URLRows* urls) {
254     urls->clear();
255     syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
256     syncer::ReadNode typed_url_root(&trans);
257     if (typed_url_root.InitByTagLookup(browser_sync::kTypedUrlTag) !=
258             syncer::BaseNode::INIT_OK)
259       return;
260
261     int64 child_id = typed_url_root.GetFirstChildId();
262     while (child_id != syncer::kInvalidId) {
263       syncer::ReadNode child_node(&trans);
264       if (child_node.InitByIdLookup(child_id) != syncer::BaseNode::INIT_OK)
265         return;
266
267       const sync_pb::TypedUrlSpecifics& typed_url(
268           child_node.GetTypedUrlSpecifics());
269       history::URLRow new_url(GURL(typed_url.url()));
270
271       new_url.set_title(UTF8ToUTF16(typed_url.title()));
272       DCHECK(typed_url.visits_size());
273       DCHECK_EQ(typed_url.visits_size(), typed_url.visit_transitions_size());
274       new_url.set_last_visit(base::Time::FromInternalValue(
275           typed_url.visits(typed_url.visits_size() - 1)));
276       new_url.set_hidden(typed_url.hidden());
277
278       urls->push_back(new_url);
279       child_id = child_node.GetSuccessorId();
280     }
281   }
282
283   void SetIdleChangeProcessorExpectations() {
284     EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).Times(0);
285     EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).Times(0);
286     EXPECT_CALL((*history_backend_.get()), GetURL(_, _)).Times(0);
287     EXPECT_CALL((*history_backend_.get()), DeleteURL(_)).Times(0);
288   }
289
290   static bool URLsEqual(history::URLRow& lhs, history::URLRow& rhs) {
291     // Only verify the fields we explicitly sync (i.e. don't verify typed_count
292     // or visit_count because we rely on the history DB to manage those values
293     // and they are left unchanged by HistoryBackendMock).
294     return (lhs.url().spec().compare(rhs.url().spec()) == 0) &&
295            (lhs.title().compare(rhs.title()) == 0) &&
296            (lhs.last_visit() == rhs.last_visit()) &&
297            (lhs.hidden() == rhs.hidden());
298   }
299
300   static history::URLRow MakeTypedUrlEntry(const char* url,
301                                            const char* title,
302                                            int typed_count,
303                                            int64 last_visit,
304                                            bool hidden,
305                                            history::VisitVector* visits) {
306     // Give each URL a unique ID, to mimic the behavior of the real database.
307     static int unique_url_id = 0;
308     GURL gurl(url);
309     URLRow history_url(gurl, ++unique_url_id);
310     history_url.set_title(UTF8ToUTF16(title));
311     history_url.set_typed_count(typed_count);
312     history_url.set_last_visit(
313         base::Time::FromInternalValue(last_visit));
314     history_url.set_hidden(hidden);
315     visits->push_back(history::VisitRow(
316         history_url.id(), history_url.last_visit(), 0,
317         content::PAGE_TRANSITION_TYPED, 0));
318     history_url.set_visit_count(visits->size());
319     return history_url;
320   }
321
322   scoped_ptr<Thread> history_thread_;
323
324   scoped_ptr<TestingProfile> profile_;
325   scoped_refptr<HistoryBackendMock> history_backend_;
326   HistoryServiceMock* history_service_;
327   browser_sync::DataTypeErrorHandlerMock error_handler_;
328 };
329
330 void AddTypedUrlEntries(ProfileSyncServiceTypedUrlTest* test,
331                         const history::URLRows& entries) {
332   test->CreateRoot(syncer::TYPED_URLS);
333   for (size_t i = 0; i < entries.size(); ++i) {
334     history::VisitVector visits;
335     visits.push_back(history::VisitRow(
336         entries[i].id(), entries[i].last_visit(), 0,
337         content::PageTransitionFromInt(0), 0));
338     test->AddTypedUrlSyncNode(entries[i], visits);
339   }
340 }
341
342 } // namespace
343
344 TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeEmptySync) {
345   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
346       WillOnce(Return(true));
347   SetIdleChangeProcessorExpectations();
348   CreateRootHelper create_root(this, syncer::TYPED_URLS);
349   TypedUrlModelAssociator* associator =
350       StartSyncService(create_root.callback());
351   history::URLRows sync_entries;
352   GetTypedUrlsFromSyncDB(&sync_entries);
353   EXPECT_EQ(0U, sync_entries.size());
354   ASSERT_EQ(0, associator->GetErrorPercentage());
355 }
356
357 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeEmptySync) {
358   history::URLRows entries;
359   history::VisitVector visits;
360   entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar",
361                                       2, 15, false, &visits));
362
363   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
364       WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
365   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
366       WillRepeatedly(DoAll(SetArgumentPointee<2>(visits), Return(true)));
367   SetIdleChangeProcessorExpectations();
368   CreateRootHelper create_root(this, syncer::TYPED_URLS);
369   TypedUrlModelAssociator* associator =
370       StartSyncService(create_root.callback());
371   history::URLRows sync_entries;
372   GetTypedUrlsFromSyncDB(&sync_entries);
373   ASSERT_EQ(1U, sync_entries.size());
374   EXPECT_TRUE(URLsEqual(entries[0], sync_entries[0]));
375   ASSERT_EQ(0, associator->GetErrorPercentage());
376 }
377
378 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeErrorReadingVisits) {
379   history::URLRows entries;
380   history::VisitVector visits;
381   history::URLRow native_entry1(MakeTypedUrlEntry("http://foo.com", "bar",
382                                                   2, 15, false, &visits));
383   history::URLRow native_entry2(MakeTypedUrlEntry("http://foo2.com", "bar",
384                                                   3, 15, false, &visits));
385   entries.push_back(native_entry1);
386   entries.push_back(native_entry2);
387   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
388       WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
389   // Return an error from GetMostRecentVisitsForURL() for the second URL.
390   EXPECT_CALL((*history_backend_.get()),
391               GetMostRecentVisitsForURL(native_entry1.id(), _, _)).
392                   WillRepeatedly(Return(true));
393   EXPECT_CALL((*history_backend_.get()),
394               GetMostRecentVisitsForURL(native_entry2.id(), _, _)).
395                   WillRepeatedly(Return(false));
396   SetIdleChangeProcessorExpectations();
397   CreateRootHelper create_root(this, syncer::TYPED_URLS);
398   StartSyncService(create_root.callback());
399   history::URLRows sync_entries;
400   GetTypedUrlsFromSyncDB(&sync_entries);
401   ASSERT_EQ(1U, sync_entries.size());
402   EXPECT_TRUE(URLsEqual(native_entry1, sync_entries[0]));
403 }
404
405 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeWithBlankEmptySync) {
406   std::vector<history::URLRow> entries;
407   history::VisitVector visits;
408   // Add an empty URL.
409   entries.push_back(MakeTypedUrlEntry("", "bar",
410                                       2, 15, false, &visits));
411   entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar",
412                                       2, 15, false, &visits));
413   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
414       WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
415   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
416       WillRepeatedly(DoAll(SetArgumentPointee<2>(visits), Return(true)));
417   SetIdleChangeProcessorExpectations();
418   CreateRootHelper create_root(this, syncer::TYPED_URLS);
419   StartSyncService(create_root.callback());
420   std::vector<history::URLRow> sync_entries;
421   GetTypedUrlsFromSyncDB(&sync_entries);
422   // The empty URL should be ignored.
423   ASSERT_EQ(1U, sync_entries.size());
424   EXPECT_TRUE(URLsEqual(entries[1], sync_entries[0]));
425 }
426
427 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncNoMerge) {
428   history::VisitVector native_visits;
429   history::VisitVector sync_visits;
430   history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry",
431                                                  2, 15, false, &native_visits));
432   history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry",
433                                                3, 16, false, &sync_visits));
434
435   history::URLRows native_entries;
436   native_entries.push_back(native_entry);
437   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
438       WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
439   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
440       WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true)));
441   EXPECT_CALL((*history_backend_.get()),
442       AddVisits(_, _, history::SOURCE_SYNCED)).WillRepeatedly(Return(true));
443
444   history::URLRows sync_entries;
445   sync_entries.push_back(sync_entry);
446
447   EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).
448       WillRepeatedly(Return(true));
449   StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
450
451   std::map<std::string, history::URLRow> expected;
452   expected[native_entry.url().spec()] = native_entry;
453   expected[sync_entry.url().spec()] = sync_entry;
454
455   history::URLRows new_sync_entries;
456   GetTypedUrlsFromSyncDB(&new_sync_entries);
457
458   EXPECT_TRUE(new_sync_entries.size() == expected.size());
459   for (history::URLRows::iterator entry = new_sync_entries.begin();
460        entry != new_sync_entries.end(); ++entry) {
461     EXPECT_TRUE(URLsEqual(expected[entry->url().spec()], *entry));
462   }
463 }
464
465 TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeExpiredSync) {
466   history::VisitVector sync_visits;
467   history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry",
468                                                3, EXPIRED_VISIT, false,
469                                                &sync_visits));
470   history::URLRows sync_entries;
471   sync_entries.push_back(sync_entry);
472
473   // Since all our URLs are expired, no backend calls to add new URLs will be
474   // made.
475   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
476       WillOnce(Return(true));
477   SetIdleChangeProcessorExpectations();
478
479   StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
480 }
481
482 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncMerge) {
483   history::VisitVector native_visits;
484   history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry",
485                                                  2, 15, false, &native_visits));
486   history::VisitVector sync_visits;
487   history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "name",
488                                                1, 17, false, &sync_visits));
489   history::VisitVector merged_visits;
490   merged_visits.push_back(history::VisitRow(
491       sync_entry.id(), base::Time::FromInternalValue(15), 0,
492       content::PageTransitionFromInt(0), 0));
493
494   history::URLRow merged_entry(MakeTypedUrlEntry("http://native.com", "name",
495                                                  2, 17, false, &merged_visits));
496
497   history::URLRows native_entries;
498   native_entries.push_back(native_entry);
499   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
500       WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
501   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
502       WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true)));
503   EXPECT_CALL((*history_backend_.get()),
504       AddVisits(_, _, history::SOURCE_SYNCED)). WillRepeatedly(Return(true));
505
506   history::URLRows sync_entries;
507   sync_entries.push_back(sync_entry);
508
509   EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).
510       WillRepeatedly(Return(true));
511   EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).
512       WillRepeatedly(Return());
513   StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
514
515   history::URLRows new_sync_entries;
516   GetTypedUrlsFromSyncDB(&new_sync_entries);
517   ASSERT_EQ(1U, new_sync_entries.size());
518   EXPECT_TRUE(URLsEqual(merged_entry, new_sync_entries[0]));
519 }
520
521 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeWithErrorHasSyncMerge) {
522   history::VisitVector native_visits;
523   history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "native",
524                                                  2, 15, false, &native_visits));
525   history::VisitVector sync_visits;
526   history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "sync",
527                                                1, 17, false, &sync_visits));
528
529   history::URLRows native_entries;
530   native_entries.push_back(native_entry);
531   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
532       WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
533   // Return an error getting the visits for the native URL.
534   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
535       WillRepeatedly(Return(false));
536   EXPECT_CALL((*history_backend_.get()), GetURL(_, _)).
537       WillRepeatedly(DoAll(SetArgumentPointee<1>(native_entry), Return(true)));
538   EXPECT_CALL((*history_backend_.get()),
539       AddVisits(_, _, history::SOURCE_SYNCED)). WillRepeatedly(Return(true));
540
541   history::URLRows sync_entries;
542   sync_entries.push_back(sync_entry);
543
544   EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).
545       WillRepeatedly(Return(true));
546   EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).
547       WillRepeatedly(Return());
548   StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
549
550   history::URLRows new_sync_entries;
551   GetTypedUrlsFromSyncDB(&new_sync_entries);
552   ASSERT_EQ(1U, new_sync_entries.size());
553   EXPECT_TRUE(URLsEqual(sync_entry, new_sync_entries[0]));
554 }
555
556 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAdd) {
557   history::VisitVector added_visits;
558   history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry",
559                                                 2, 15, false, &added_visits));
560
561   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
562       WillOnce(Return(true));
563   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
564       WillOnce(DoAll(SetArgumentPointee<2>(added_visits), Return(true)));
565
566   SetIdleChangeProcessorExpectations();
567   CreateRootHelper create_root(this, syncer::TYPED_URLS);
568   StartSyncService(create_root.callback());
569
570   history::URLsModifiedDetails details;
571   details.changed_urls.push_back(added_entry);
572   scoped_refptr<ThreadNotifier> notifier(
573       new ThreadNotifier(history_thread_.get()));
574   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
575                    content::Source<Profile>(profile_.get()),
576                    content::Details<history::URLsModifiedDetails>(&details));
577
578   history::URLRows new_sync_entries;
579   GetTypedUrlsFromSyncDB(&new_sync_entries);
580   ASSERT_EQ(1U, new_sync_entries.size());
581   EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0]));
582 }
583
584 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAddWithBlank) {
585   history::VisitVector added_visits;
586   history::URLRow empty_entry(MakeTypedUrlEntry("", "entry",
587                                                 2, 15, false, &added_visits));
588   history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry",
589                                                 2, 15, false, &added_visits));
590
591   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
592       WillOnce(Return(true));
593   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
594       WillRepeatedly(DoAll(SetArgumentPointee<2>(added_visits), Return(true)));
595
596   SetIdleChangeProcessorExpectations();
597   CreateRootHelper create_root(this, syncer::TYPED_URLS);
598   StartSyncService(create_root.callback());
599
600   history::URLsModifiedDetails details;
601   details.changed_urls.push_back(empty_entry);
602   details.changed_urls.push_back(added_entry);
603   scoped_refptr<ThreadNotifier> notifier(
604       new ThreadNotifier(history_thread_.get()));
605   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
606                    content::Source<Profile>(profile_.get()),
607                    content::Details<history::URLsModifiedDetails>(&details));
608
609   std::vector<history::URLRow> new_sync_entries;
610   GetTypedUrlsFromSyncDB(&new_sync_entries);
611   ASSERT_EQ(1U, new_sync_entries.size());
612   EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0]));
613 }
614
615 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdate) {
616   history::VisitVector original_visits;
617   history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry",
618                                                    2, 15, false,
619                                                    &original_visits));
620   history::URLRows original_entries;
621   original_entries.push_back(original_entry);
622
623   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
624       WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
625   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
626       WillOnce(DoAll(SetArgumentPointee<2>(original_visits),
627                      Return(true)));
628   CreateRootHelper create_root(this, syncer::TYPED_URLS);
629   StartSyncService(create_root.callback());
630
631   history::VisitVector updated_visits;
632   history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry",
633                                                   7, 17, false,
634                                                   &updated_visits));
635   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
636       WillOnce(DoAll(SetArgumentPointee<2>(updated_visits),
637                      Return(true)));
638
639   history::URLsModifiedDetails details;
640   details.changed_urls.push_back(updated_entry);
641   scoped_refptr<ThreadNotifier> notifier(
642       new ThreadNotifier(history_thread_.get()));
643   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
644                    content::Source<Profile>(profile_.get()),
645                    content::Details<history::URLsModifiedDetails>(&details));
646
647   history::URLRows new_sync_entries;
648   GetTypedUrlsFromSyncDB(&new_sync_entries);
649   ASSERT_EQ(1U, new_sync_entries.size());
650   EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0]));
651 }
652
653 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAddFromVisit) {
654   history::VisitVector added_visits;
655   history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry",
656                                                 2, 15, false, &added_visits));
657
658   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
659       WillOnce(Return(true));
660   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
661       WillOnce(DoAll(SetArgumentPointee<2>(added_visits), Return(true)));
662
663   SetIdleChangeProcessorExpectations();
664   CreateRootHelper create_root(this, syncer::TYPED_URLS);
665   StartSyncService(create_root.callback());
666
667   history::URLVisitedDetails details;
668   details.row = added_entry;
669   details.transition = content::PAGE_TRANSITION_TYPED;
670   scoped_refptr<ThreadNotifier> notifier(
671       new ThreadNotifier(history_thread_.get()));
672   notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
673                    content::Source<Profile>(profile_.get()),
674                    content::Details<history::URLVisitedDetails>(&details));
675
676   history::URLRows new_sync_entries;
677   GetTypedUrlsFromSyncDB(&new_sync_entries);
678   ASSERT_EQ(1U, new_sync_entries.size());
679   EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0]));
680 }
681
682 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdateFromVisit) {
683   history::VisitVector original_visits;
684   history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry",
685                                                    2, 15, false,
686                                                    &original_visits));
687   history::URLRows original_entries;
688   original_entries.push_back(original_entry);
689
690   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
691       WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
692   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
693       WillOnce(DoAll(SetArgumentPointee<2>(original_visits),
694                            Return(true)));
695   CreateRootHelper create_root(this, syncer::TYPED_URLS);
696   StartSyncService(create_root.callback());
697
698   history::VisitVector updated_visits;
699   history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry",
700                                                   7, 17, false,
701                                                   &updated_visits));
702   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
703       WillOnce(DoAll(SetArgumentPointee<2>(updated_visits),
704                            Return(true)));
705
706   history::URLVisitedDetails details;
707   details.row = updated_entry;
708   details.transition = content::PAGE_TRANSITION_TYPED;
709   scoped_refptr<ThreadNotifier> notifier(
710       new ThreadNotifier(history_thread_.get()));
711   notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
712                    content::Source<Profile>(profile_.get()),
713                    content::Details<history::URLVisitedDetails>(&details));
714
715   history::URLRows new_sync_entries;
716   GetTypedUrlsFromSyncDB(&new_sync_entries);
717   ASSERT_EQ(1U, new_sync_entries.size());
718   EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0]));
719 }
720
721 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserIgnoreChangeUpdateFromVisit) {
722   history::VisitVector original_visits;
723   history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry",
724                                                    2, 15, false,
725                                                    &original_visits));
726   history::URLRows original_entries;
727   original_entries.push_back(original_entry);
728
729   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
730       WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
731   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
732       WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits),
733                            Return(true)));
734   CreateRootHelper create_root(this, syncer::TYPED_URLS);
735   StartSyncService(create_root.callback());
736   history::URLRows new_sync_entries;
737   GetTypedUrlsFromSyncDB(&new_sync_entries);
738   ASSERT_EQ(1U, new_sync_entries.size());
739   EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0]));
740
741   history::VisitVector updated_visits;
742   history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry",
743                                                   7, 15, false,
744                                                   &updated_visits));
745   history::URLVisitedDetails details;
746   details.row = updated_entry;
747
748   // Should ignore this change because it's not TYPED.
749   details.transition = content::PAGE_TRANSITION_RELOAD;
750   scoped_refptr<ThreadNotifier> notifier(
751       new ThreadNotifier(history_thread_.get()));
752   notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
753                    content::Source<Profile>(profile_.get()),
754                    content::Details<history::URLVisitedDetails>(&details));
755
756   GetTypedUrlsFromSyncDB(&new_sync_entries);
757
758   // Should be no changes to the sync DB from this notification.
759   ASSERT_EQ(1U, new_sync_entries.size());
760   EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0]));
761
762   // Now, try updating it with a large number of visits not divisible by 10
763   // (should ignore this visit).
764   history::URLRow twelve_visits(MakeTypedUrlEntry("http://mine.com", "entry",
765                                                   12, 15, false,
766                                                   &updated_visits));
767   details.row = twelve_visits;
768   details.transition = content::PAGE_TRANSITION_TYPED;
769   notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
770                    content::Source<Profile>(profile_.get()),
771                    content::Details<history::URLVisitedDetails>(&details));
772   GetTypedUrlsFromSyncDB(&new_sync_entries);
773   // Should be no changes to the sync DB from this notification.
774   ASSERT_EQ(1U, new_sync_entries.size());
775   EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0]));
776
777   // Now, try updating it with a large number of visits that is divisible by 10
778   // (should *not* be ignored).
779   history::URLRow twenty_visits(MakeTypedUrlEntry("http://mine.com", "entry",
780                                                   20, 15, false,
781                                                   &updated_visits));
782   details.row = twenty_visits;
783   details.transition = content::PAGE_TRANSITION_TYPED;
784   notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
785                    content::Source<Profile>(profile_.get()),
786                    content::Details<history::URLVisitedDetails>(&details));
787   GetTypedUrlsFromSyncDB(&new_sync_entries);
788   ASSERT_EQ(1U, new_sync_entries.size());
789   EXPECT_TRUE(URLsEqual(twenty_visits, new_sync_entries[0]));
790 }
791
792 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemove) {
793   history::VisitVector original_visits1;
794   history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry",
795                                                     2, 15, false,
796                                                     &original_visits1));
797   history::VisitVector original_visits2;
798   history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com",
799                                                     "entry2",
800                                                     3, 15, false,
801                                                     &original_visits2));
802   history::URLRows original_entries;
803   original_entries.push_back(original_entry1);
804   original_entries.push_back(original_entry2);
805
806   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
807       WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
808   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
809       WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1),
810                            Return(true)));
811   CreateRootHelper create_root(this, syncer::TYPED_URLS);
812   StartSyncService(create_root.callback());
813
814   history::URLsDeletedDetails changes;
815   changes.all_history = false;
816   changes.rows.push_back(history::URLRow(GURL("http://mine.com")));
817   scoped_refptr<ThreadNotifier> notifier(
818       new ThreadNotifier(history_thread_.get()));
819   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
820                    content::Source<Profile>(profile_.get()),
821                    content::Details<history::URLsDeletedDetails>(&changes));
822
823   history::URLRows new_sync_entries;
824   GetTypedUrlsFromSyncDB(&new_sync_entries);
825   ASSERT_EQ(1U, new_sync_entries.size());
826   EXPECT_TRUE(URLsEqual(original_entry2, new_sync_entries[0]));
827 }
828
829 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveArchive) {
830   history::VisitVector original_visits1;
831   history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry",
832                                                     2, 15, false,
833                                                     &original_visits1));
834   history::VisitVector original_visits2;
835   history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com",
836                                                     "entry2",
837                                                     3, 15, false,
838                                                     &original_visits2));
839   history::URLRows original_entries;
840   original_entries.push_back(original_entry1);
841   original_entries.push_back(original_entry2);
842
843   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
844       WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
845   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
846       WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1),
847                            Return(true)));
848   CreateRootHelper create_root(this, syncer::TYPED_URLS);
849   StartSyncService(create_root.callback());
850
851   history::URLsDeletedDetails changes;
852   changes.all_history = false;
853   // Setting archived=true should cause the sync code to ignore this deletion.
854   changes.archived = true;
855   changes.rows.push_back(history::URLRow(GURL("http://mine.com")));
856   scoped_refptr<ThreadNotifier> notifier(
857       new ThreadNotifier(history_thread_.get()));
858   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
859                    content::Source<Profile>(profile_.get()),
860                    content::Details<history::URLsDeletedDetails>(&changes));
861
862   history::URLRows new_sync_entries;
863   GetTypedUrlsFromSyncDB(&new_sync_entries);
864   // Both URLs should still be there.
865   ASSERT_EQ(2U, new_sync_entries.size());
866 }
867
868 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveAll) {
869   history::VisitVector original_visits1;
870   history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry",
871                                                     2, 15, false,
872                                                     &original_visits1));
873   history::VisitVector original_visits2;
874   history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com",
875                                                     "entry2",
876                                                     3, 15, false,
877                                                     &original_visits2));
878   history::URLRows original_entries;
879   original_entries.push_back(original_entry1);
880   original_entries.push_back(original_entry2);
881
882   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
883       WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
884   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
885       WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1),
886                            Return(true)));
887   CreateRootHelper create_root(this, syncer::TYPED_URLS);
888   StartSyncService(create_root.callback());
889
890   history::URLRows new_sync_entries;
891   GetTypedUrlsFromSyncDB(&new_sync_entries);
892   ASSERT_EQ(2U, new_sync_entries.size());
893
894   history::URLsDeletedDetails changes;
895   changes.all_history = true;
896   scoped_refptr<ThreadNotifier> notifier(
897       new ThreadNotifier(history_thread_.get()));
898   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
899                    content::Source<Profile>(profile_.get()),
900                    content::Details<history::URLsDeletedDetails>(&changes));
901
902   GetTypedUrlsFromSyncDB(&new_sync_entries);
903   ASSERT_EQ(0U, new_sync_entries.size());
904 }
905
906 TEST_F(ProfileSyncServiceTypedUrlTest, FailWriteToHistoryBackend) {
907   history::VisitVector native_visits;
908   history::VisitVector sync_visits;
909   history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry",
910                                                  2, 15, false, &native_visits));
911   history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry",
912                                                3, 16, false, &sync_visits));
913
914   history::URLRows native_entries;
915   native_entries.push_back(native_entry);
916   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
917       WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
918   EXPECT_CALL((*history_backend_.get()), GetURL(_, _)).
919       WillOnce(DoAll(SetArgumentPointee<1>(native_entry), Return(true)));
920   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
921       WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true)));
922   EXPECT_CALL((*history_backend_.get()),
923       AddVisits(_, _, history::SOURCE_SYNCED)).WillRepeatedly(Return(false));
924
925   history::URLRows sync_entries;
926   sync_entries.push_back(sync_entry);
927
928   EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).
929       WillRepeatedly(Return(false));
930   TypedUrlModelAssociator* associator =
931       StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
932   // Errors writing to the DB should be recorded, but should not cause an
933   // unrecoverable error.
934   ASSERT_FALSE(
935       sync_service_->failed_data_types_handler().GetFailedTypes().Has(
936           syncer::TYPED_URLS));
937   // Some calls should have succeeded, so the error percentage should be
938   // somewhere > 0 and < 100.
939   ASSERT_NE(0, associator->GetErrorPercentage());
940   ASSERT_NE(100, associator->GetErrorPercentage());
941 }
942
943 TEST_F(ProfileSyncServiceTypedUrlTest, FailToGetTypedURLs) {
944   history::VisitVector native_visits;
945   history::VisitVector sync_visits;
946   history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry",
947                                                  2, 15, false, &native_visits));
948   history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry",
949                                                3, 16, false, &sync_visits));
950
951   history::URLRows native_entries;
952   native_entries.push_back(native_entry);
953   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
954       WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(false)));
955
956   history::URLRows sync_entries;
957   sync_entries.push_back(sync_entry);
958
959   EXPECT_CALL(error_handler_, CreateAndUploadError(_, _, _)).
960               WillOnce(Return(syncer::SyncError(
961                                   FROM_HERE,
962                                   syncer::SyncError::DATATYPE_ERROR,
963                                   "Unit test",
964                                   syncer::TYPED_URLS)));
965   StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
966   // Errors getting typed URLs will cause an unrecoverable error (since we can
967   // do *nothing* in that case).
968   ASSERT_TRUE(
969       sync_service_->failed_data_types_handler().GetFailedTypes().Has(
970           syncer::TYPED_URLS));
971   ASSERT_EQ(
972       1u, sync_service_->failed_data_types_handler().GetFailedTypes().Size());
973   // Can't check GetErrorPercentage(), because generating an unrecoverable
974   // error will free the model associator.
975 }
976
977 TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalFileURL) {
978   history::VisitVector original_visits;
979   // Create http and file url.
980   history::URLRow url_entry(MakeTypedUrlEntry("http://yey.com",
981                                               "yey", 12, 15, false,
982                                               &original_visits));
983   history::URLRow file_entry(MakeTypedUrlEntry("file:///kitty.jpg",
984                                                "kitteh", 12, 15, false,
985                                                &original_visits));
986
987   history::URLRows original_entries;
988   original_entries.push_back(url_entry);
989   original_entries.push_back(file_entry);
990
991   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
992       WillRepeatedly(DoAll(SetArgumentPointee<0>(original_entries),
993                      Return(true)));
994   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
995       WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits),
996                      Return(true)));
997   CreateRootHelper create_root(this, syncer::TYPED_URLS);
998   StartSyncService(create_root.callback());
999
1000   history::VisitVector updated_visits;
1001   // Create updates for the previous urls + a new file one.
1002   history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com",
1003                                                       "yey", 20, 15, false,
1004                                                       &updated_visits));
1005   history::URLRow updated_file_entry(MakeTypedUrlEntry("file:///cat.jpg",
1006                                                        "cat", 20, 15, false,
1007                                                        &updated_visits));
1008   history::URLRow new_file_entry(MakeTypedUrlEntry("file:///dog.jpg",
1009                                                    "dog", 20, 15, false,
1010                                                    &updated_visits));
1011   history::URLsModifiedDetails details;
1012   details.changed_urls.push_back(updated_url_entry);
1013   details.changed_urls.push_back(updated_file_entry);
1014   details.changed_urls.push_back(new_file_entry);
1015   scoped_refptr<ThreadNotifier> notifier(
1016       new ThreadNotifier(history_thread_.get()));
1017   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
1018                    content::Source<Profile>(profile_.get()),
1019                    content::Details<history::URLsModifiedDetails>(&details));
1020
1021   history::URLRows new_sync_entries;
1022   GetTypedUrlsFromSyncDB(&new_sync_entries);
1023
1024   // We should ignore the local file urls (existing and updated),
1025   // and only be left with the updated http url.
1026   ASSERT_EQ(1U, new_sync_entries.size());
1027   EXPECT_TRUE(URLsEqual(updated_url_entry, new_sync_entries[0]));
1028 }
1029
1030 TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalhostURL) {
1031   history::VisitVector original_visits;
1032   // Create http and localhost url.
1033   history::URLRow url_entry(MakeTypedUrlEntry("http://yey.com",
1034                                               "yey", 12, 15, false,
1035                                               &original_visits));
1036   history::URLRow localhost_entry(MakeTypedUrlEntry("http://localhost",
1037                                               "localhost", 12, 15, false,
1038                                               &original_visits));
1039
1040   history::URLRows original_entries;
1041   original_entries.push_back(url_entry);
1042   original_entries.push_back(localhost_entry);
1043
1044   EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
1045       WillRepeatedly(DoAll(SetArgumentPointee<0>(original_entries),
1046                      Return(true)));
1047   EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
1048       WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits),
1049                      Return(true)));
1050   CreateRootHelper create_root(this, syncer::TYPED_URLS);
1051   StartSyncService(create_root.callback());
1052
1053   history::VisitVector updated_visits;
1054   // Update the previous entries and add a new localhost.
1055   history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com",
1056                                                   "yey", 20, 15, false,
1057                                                   &updated_visits));
1058   history::URLRow updated_localhost_entry(MakeTypedUrlEntry(
1059                                                   "http://localhost:80",
1060                                                   "localhost", 20, 15, false,
1061                                                   &original_visits));
1062   history::URLRow localhost_ip_entry(MakeTypedUrlEntry("http://127.0.0.1",
1063                                                   "localhost", 12, 15, false,
1064                                                   &original_visits));
1065   history::URLsModifiedDetails details;
1066   details.changed_urls.push_back(updated_url_entry);
1067   details.changed_urls.push_back(updated_localhost_entry);
1068   details.changed_urls.push_back(localhost_ip_entry);
1069   scoped_refptr<ThreadNotifier> notifier(
1070       new ThreadNotifier(history_thread_.get()));
1071   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
1072                    content::Source<Profile>(profile_.get()),
1073                    content::Details<history::URLsModifiedDetails>(&details));
1074
1075   history::URLRows new_sync_entries;
1076   GetTypedUrlsFromSyncDB(&new_sync_entries);
1077
1078   // We should ignore the localhost urls and left only with http url.
1079   ASSERT_EQ(1U, new_sync_entries.size());
1080   EXPECT_TRUE(URLsEqual(updated_url_entry, new_sync_entries[0]));
1081 }