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