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