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