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