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