Home | History | Annotate | Download | only in history
      1 // Copyright (c) 2013 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 "chrome/browser/history/typed_url_syncable_service.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/memory/ref_counted.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome/browser/history/history_backend.h"
     12 #include "chrome/browser/history/history_types.h"
     13 #include "content/public/browser/notification_types.h"
     14 #include "sync/api/sync_error.h"
     15 #include "sync/api/sync_error_factory_mock.h"
     16 #include "sync/protocol/sync.pb.h"
     17 #include "sync/protocol/typed_url_specifics.pb.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 using history::HistoryBackend;
     21 using history::URLID;
     22 using history::URLRow;
     23 using history::URLRows;
     24 using history::VisitRow;
     25 using history::VisitVector;
     26 
     27 namespace {
     28 
     29 // Constants used to limit size of visits processed.
     30 static const int kMaxTypedUrlVisits = 100;
     31 static const int kMaxVisitsToFetch = 1000;
     32 
     33 // Visits with this timestamp are treated as expired.
     34 static const int EXPIRED_VISIT = -1;
     35 
     36 // TestChangeProcessor --------------------------------------------------------
     37 
     38 class TestChangeProcessor : public syncer::SyncChangeProcessor {
     39  public:
     40   TestChangeProcessor() : change_output_(NULL) {}
     41 
     42   // syncer::SyncChangeProcessor implementation.
     43   virtual syncer::SyncError ProcessSyncChanges(
     44       const tracked_objects::Location& from_here,
     45       const syncer::SyncChangeList& change_list) OVERRIDE {
     46     change_output_->insert(change_output_->end(), change_list.begin(),
     47                            change_list.end());
     48     return syncer::SyncError();
     49   }
     50 
     51   // Set pointer location to write SyncChanges to in ProcessSyncChanges.
     52   void SetChangeOutput(syncer::SyncChangeList *change_output) {
     53     change_output_ = change_output;
     54   }
     55 
     56  private:
     57   syncer::SyncChangeList *change_output_;
     58 
     59   DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor);
     60 };
     61 
     62 // TestHistoryBackend ----------------------------------------------------------
     63 
     64 class TestHistoryBackend : public HistoryBackend {
     65  public:
     66   TestHistoryBackend() : HistoryBackend(base::FilePath(), 0, NULL, NULL) {}
     67 
     68   // HistoryBackend test implementation.
     69   virtual bool IsExpiredVisitTime(const base::Time& time) OVERRIDE {
     70     return time.ToInternalValue() == EXPIRED_VISIT;
     71   }
     72 
     73   virtual bool GetMostRecentVisitsForURL(
     74       URLID id,
     75       int max_visits,
     76       VisitVector* visits) OVERRIDE {
     77     if (local_db_visits_[id].empty())
     78       return false;
     79 
     80     visits->insert(visits->end(),
     81                    local_db_visits_[id].begin(),
     82                    local_db_visits_[id].end());
     83     return true;
     84   }
     85 
     86   // Helpers.
     87   void SetVisitsForUrl(URLID id, VisitVector* visits) {
     88     if (!local_db_visits_[id].empty()) {
     89       local_db_visits_[id].clear();
     90     }
     91 
     92     local_db_visits_[id].insert(local_db_visits_[id].end(),
     93                                 visits->begin(),
     94                                 visits->end());
     95   }
     96 
     97   void DeleteVisitsForUrl(const URLID& id) {
     98     local_db_visits_.erase(id);
     99   }
    100 
    101  private:
    102   virtual ~TestHistoryBackend() {}
    103 
    104   // Mock of visit table in local db.
    105   std::map<URLID, VisitVector> local_db_visits_;
    106 };
    107 
    108 }  // namespace
    109 
    110 namespace history {
    111 
    112 // TypedUrlSyncableServiceTest -------------------------------------------------
    113 
    114 class TypedUrlSyncableServiceTest : public testing::Test {
    115  public:
    116   // Create a new row object and add a typed visit to the |visits| vector.
    117   // Note that the real history db returns visits in reverse chronological
    118   // order, so |visits| is treated this way. If the newest (first) visit
    119   // in visits does not match |last_visit|, then a typed visit for this
    120   // time is prepended to the front (or if |last_visit| is too old, it is
    121   // set equal to the time of the newest visit).
    122   static URLRow MakeTypedUrlRow(const char* url,
    123                                 const char* title,
    124                                 int typed_count,
    125                                 int64 last_visit,
    126                                 bool hidden,
    127                                 VisitVector* visits);
    128 
    129   static void AddNewestVisit(URLRow* url,
    130                              VisitVector* visits,
    131                              content::PageTransition transition,
    132                              int64 visit_time);
    133 
    134   static void AddOldestVisit(URLRow* url,
    135                              VisitVector* visits,
    136                              content::PageTransition transition,
    137                              int64 visit_time);
    138 
    139   static bool URLsEqual(URLRow& row,
    140                         sync_pb::TypedUrlSpecifics& specifics) {
    141     return ((row.url().spec().compare(specifics.url()) == 0) &&
    142             (UTF16ToUTF8(row.title()).compare(specifics.title()) == 0) &&
    143             (row.hidden() == specifics.hidden()));
    144   }
    145 
    146   bool InitiateServerState(
    147       unsigned int num_typed_urls,
    148       unsigned int num_reload_urls,
    149       URLRows* rows,
    150       std::vector<VisitVector>* visit_vectors,
    151       const std::vector<const char*>& urls,
    152       syncer::SyncChangeList* change_list);
    153 
    154  protected:
    155   TypedUrlSyncableServiceTest() {}
    156 
    157   virtual ~TypedUrlSyncableServiceTest() {}
    158 
    159   virtual void SetUp() OVERRIDE {
    160     fake_history_backend_ = new TestHistoryBackend();
    161     typed_url_sync_service_.reset(
    162         new TypedUrlSyncableService(fake_history_backend_.get()));
    163     fake_change_processor_.reset(new TestChangeProcessor);
    164   }
    165 
    166   scoped_refptr<HistoryBackend> fake_history_backend_;
    167   scoped_ptr<TypedUrlSyncableService> typed_url_sync_service_;
    168   scoped_ptr<syncer::SyncChangeProcessor> fake_change_processor_;
    169 };
    170 
    171 URLRow TypedUrlSyncableServiceTest::MakeTypedUrlRow(
    172     const char* url,
    173     const char* title,
    174     int typed_count,
    175     int64 last_visit,
    176     bool hidden,
    177     VisitVector* visits) {
    178   DCHECK(visits->empty());
    179 
    180   // Give each URL a unique ID, to mimic the behavior of the real database.
    181   static int unique_url_id = 0;
    182   GURL gurl(url);
    183   URLRow history_url(gurl, ++unique_url_id);
    184   history_url.set_title(UTF8ToUTF16(title));
    185   history_url.set_typed_count(typed_count);
    186   history_url.set_hidden(hidden);
    187 
    188   base::Time last_visit_time = base::Time::FromInternalValue(last_visit);
    189   history_url.set_last_visit(last_visit_time);
    190 
    191   VisitVector::iterator first = visits->begin();
    192   if (typed_count > 0) {
    193     // Add a typed visit for time |last_visit|.
    194     visits->insert(first,
    195                    VisitRow(history_url.id(), last_visit_time, 0,
    196                             content::PAGE_TRANSITION_TYPED, 0));
    197   } else {
    198     // Add a non-typed visit for time |last_visit|.
    199     visits->insert(first,
    200                    VisitRow(history_url.id(), last_visit_time, 0,
    201                             content::PAGE_TRANSITION_RELOAD, 0));
    202   }
    203 
    204   history_url.set_visit_count(visits->size());
    205   return history_url;
    206 }
    207 
    208 void TypedUrlSyncableServiceTest::AddNewestVisit(
    209     URLRow* url,
    210     VisitVector* visits,
    211     content::PageTransition transition,
    212     int64 visit_time) {
    213   base::Time time = base::Time::FromInternalValue(visit_time);
    214   visits->insert(visits->begin(),
    215                  VisitRow(url->id(), time, 0, transition, 0));
    216 
    217   if (transition == content::PAGE_TRANSITION_TYPED) {
    218     url->set_typed_count(url->typed_count() + 1);
    219   }
    220 
    221   url->set_last_visit(time);
    222   url->set_visit_count(visits->size());
    223 }
    224 
    225 void TypedUrlSyncableServiceTest::AddOldestVisit(
    226     URLRow* url,
    227     VisitVector* visits,
    228     content::PageTransition transition,
    229     int64 visit_time) {
    230   base::Time time = base::Time::FromInternalValue(visit_time);
    231   visits->push_back(VisitRow(url->id(), time, 0, transition, 0));
    232 
    233   if (transition == content::PAGE_TRANSITION_TYPED) {
    234     url->set_typed_count(url->typed_count() + 1);
    235   }
    236 
    237   url->set_visit_count(visits->size());
    238 }
    239 
    240 bool TypedUrlSyncableServiceTest::InitiateServerState(
    241     unsigned int num_typed_urls,
    242     unsigned int num_reload_urls,
    243     URLRows* rows,
    244     std::vector<VisitVector>* visit_vectors,
    245     const std::vector<const char*>& urls,
    246     syncer::SyncChangeList* change_list) {
    247   unsigned int total_urls = num_typed_urls + num_reload_urls;
    248   DCHECK(urls.size() >= total_urls);
    249   if (!typed_url_sync_service_.get())
    250     return false;
    251 
    252   static_cast<TestChangeProcessor*>(fake_change_processor_.get())->
    253       SetChangeOutput(change_list);
    254 
    255   // Set change processor.
    256   syncer::SyncMergeResult result =
    257       typed_url_sync_service_->MergeDataAndStartSyncing(
    258           syncer::TYPED_URLS,
    259           syncer::SyncDataList(),
    260           fake_change_processor_.Pass(),
    261           scoped_ptr<syncer::SyncErrorFactory>(
    262               new syncer::SyncErrorFactoryMock()));
    263   EXPECT_FALSE(result.error().IsSet()) << result.error().message();
    264 
    265   if (total_urls) {
    266     // Create new URL rows, populate the mock backend with its visits, and
    267     // send to the sync service.
    268     URLRows changed_urls;
    269 
    270     for (unsigned int i = 0; i < total_urls; ++i) {
    271       int typed = i < num_typed_urls ? 1 : 0;
    272       VisitVector visits;
    273       visit_vectors->push_back(visits);
    274       rows->push_back(MakeTypedUrlRow(
    275           urls[i], "pie", typed, i + 3, false, &visit_vectors->back()));
    276       static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
    277           SetVisitsForUrl(rows->back().id(), &visit_vectors->back());
    278       changed_urls.push_back(rows->back());
    279     }
    280 
    281     typed_url_sync_service_->OnUrlsModified(&changed_urls);
    282   }
    283   // Check that communication with sync was successful.
    284   if (num_typed_urls != change_list->size())
    285     return false;
    286   return true;
    287 }
    288 
    289 TEST_F(TypedUrlSyncableServiceTest, AddLocalTypedUrlAndSync) {
    290   // Create a local typed URL (simulate a typed visit) that is not already
    291   // in sync. Check that sync is sent an ADD change for the existing URL.
    292   syncer::SyncChangeList change_list;
    293 
    294   URLRows url_rows;
    295   std::vector<VisitVector> visit_vectors;
    296   std::vector<const char*> urls;
    297   urls.push_back("http://pie.com/");
    298 
    299   ASSERT_TRUE(
    300       InitiateServerState(1, 0, &url_rows, &visit_vectors, urls, &change_list));
    301 
    302   URLRow url_row = url_rows.front();
    303   VisitVector visits = visit_vectors.front();
    304 
    305   // Check change processor.
    306   ASSERT_EQ(1u, change_list.size());
    307   ASSERT_TRUE(change_list[0].IsValid());
    308   EXPECT_EQ(syncer::TYPED_URLS, change_list[0].sync_data().GetDataType());
    309   EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change_list[0].change_type());
    310 
    311   // Get typed url specifics.
    312   sync_pb::TypedUrlSpecifics url_specifics =
    313       change_list[0].sync_data().GetSpecifics().typed_url();
    314 
    315   EXPECT_TRUE(URLsEqual(url_row, url_specifics));
    316   ASSERT_EQ(1, url_specifics.visits_size());
    317   ASSERT_EQ(static_cast<const int>(visits.size()), url_specifics.visits_size());
    318   EXPECT_EQ(visits[0].visit_time.ToInternalValue(), url_specifics.visits(0));
    319   EXPECT_EQ(static_cast<const int>(visits[0].transition),
    320             url_specifics.visit_transitions(0));
    321 
    322   // Check that in-memory representation of sync state is accurate.
    323   std::set<GURL> sync_state;
    324   typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
    325   EXPECT_FALSE(sync_state.empty());
    326   EXPECT_EQ(1u, sync_state.size());
    327   EXPECT_TRUE(sync_state.end() != sync_state.find(url_row.url()));
    328 }
    329 
    330 TEST_F(TypedUrlSyncableServiceTest, UpdateLocalTypedUrlAndSync) {
    331   syncer::SyncChangeList change_list;
    332 
    333   URLRows url_rows;
    334   std::vector<VisitVector> visit_vectors;
    335   std::vector<const char*> urls;
    336   urls.push_back("http://pie.com/");
    337 
    338   ASSERT_TRUE(
    339       InitiateServerState(1, 0, &url_rows, &visit_vectors, urls, &change_list));
    340   change_list.clear();
    341 
    342   // Update the URL row, adding another typed visit to the visit vector.
    343   URLRow url_row = url_rows.front();
    344   VisitVector visits = visit_vectors.front();
    345 
    346   URLRows changed_urls;
    347   AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_TYPED, 7);
    348   static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
    349       SetVisitsForUrl(url_row.id(), &visits);
    350   changed_urls.push_back(url_row);
    351 
    352   // Notify typed url sync service of the update.
    353   typed_url_sync_service_->OnUrlsModified(&changed_urls);
    354 
    355   ASSERT_EQ(1u, change_list.size());
    356   ASSERT_TRUE(change_list[0].IsValid());
    357   EXPECT_EQ(syncer::TYPED_URLS, change_list[0].sync_data().GetDataType());
    358   EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change_list[0].change_type());
    359 
    360   sync_pb::TypedUrlSpecifics url_specifics =
    361       change_list[0].sync_data().GetSpecifics().typed_url();
    362 
    363   EXPECT_TRUE(URLsEqual(url_row, url_specifics));
    364   ASSERT_EQ(2, url_specifics.visits_size());
    365   ASSERT_EQ(static_cast<const int>(visits.size()), url_specifics.visits_size());
    366 
    367   // Check that each visit has been translated/communicated correctly.
    368   // Note that the specifics record visits in chronological order, and the
    369   // visits from the db are in reverse chronological order.
    370   EXPECT_EQ(visits[0].visit_time.ToInternalValue(), url_specifics.visits(1));
    371   EXPECT_EQ(static_cast<const int>(visits[0].transition),
    372             url_specifics.visit_transitions(1));
    373   EXPECT_EQ(visits[1].visit_time.ToInternalValue(), url_specifics.visits(0));
    374   EXPECT_EQ(static_cast<const int>(visits[1].transition),
    375             url_specifics.visit_transitions(0));
    376 
    377   // Check that in-memory representation of sync state is accurate.
    378   std::set<GURL> sync_state;
    379   typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
    380   EXPECT_FALSE(sync_state.empty());
    381   EXPECT_EQ(1u, sync_state.size());
    382   EXPECT_TRUE(sync_state.end() != sync_state.find(url_row.url()));
    383 }
    384 
    385 TEST_F(TypedUrlSyncableServiceTest, LinkVisitLocalTypedUrlAndSync) {
    386   syncer::SyncChangeList change_list;
    387 
    388   URLRows url_rows;
    389   std::vector<VisitVector> visit_vectors;
    390   std::vector<const char*> urls;
    391   urls.push_back("http://pie.com/");
    392 
    393   ASSERT_TRUE(
    394       InitiateServerState(1, 0, &url_rows, &visit_vectors, urls, &change_list));
    395   change_list.clear();
    396 
    397   URLRow url_row = url_rows.front();
    398   VisitVector visits = visit_vectors.front();
    399 
    400   // Update the URL row, adding a non-typed visit to the visit vector.
    401   AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_LINK, 6);
    402   static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
    403       SetVisitsForUrl(url_row.id(), &visits);
    404 
    405   content::PageTransition transition = content::PAGE_TRANSITION_LINK;
    406   // Notify typed url sync service of non-typed visit, expect no change.
    407   typed_url_sync_service_->OnUrlVisited(transition, &url_row);
    408   ASSERT_EQ(0u, change_list.size());
    409 }
    410 
    411 TEST_F(TypedUrlSyncableServiceTest, TypedVisitLocalTypedUrlAndSync) {
    412   syncer::SyncChangeList change_list;
    413 
    414   URLRows url_rows;
    415   std::vector<VisitVector> visit_vectors;
    416   std::vector<const char*> urls;
    417   urls.push_back("http://pie.com/");
    418 
    419   ASSERT_TRUE(
    420       InitiateServerState(1, 0, &url_rows, &visit_vectors, urls, &change_list));
    421   change_list.clear();
    422 
    423   URLRow url_row = url_rows.front();
    424   VisitVector visits = visit_vectors.front();
    425 
    426   // Update the URL row, adding another typed visit to the visit vector.
    427   AddOldestVisit(&url_row, &visits, content::PAGE_TRANSITION_LINK, 1);
    428   AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_LINK, 6);
    429   AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_TYPED, 7);
    430   static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
    431       SetVisitsForUrl(url_row.id(), &visits);
    432 
    433   // Notify typed url sync service of typed visit.
    434   content::PageTransition transition = content::PAGE_TRANSITION_TYPED;
    435   typed_url_sync_service_->OnUrlVisited(transition, &url_row);
    436 
    437   ASSERT_EQ(1u, change_list.size());
    438   ASSERT_TRUE(change_list[0].IsValid());
    439   EXPECT_EQ(syncer::TYPED_URLS, change_list[0].sync_data().GetDataType());
    440   EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change_list[0].change_type());
    441 
    442   sync_pb::TypedUrlSpecifics url_specifics =
    443       change_list[0].sync_data().GetSpecifics().typed_url();
    444 
    445   EXPECT_TRUE(URLsEqual(url_row, url_specifics));
    446   ASSERT_EQ(4u, visits.size());
    447   EXPECT_EQ(static_cast<const int>(visits.size()), url_specifics.visits_size());
    448 
    449   // Check that each visit has been translated/communicated correctly.
    450   // Note that the specifics record visits in chronological order, and the
    451   // visits from the db are in reverse chronological order.
    452   int r = url_specifics.visits_size() - 1;
    453   for (int i = 0; i < url_specifics.visits_size(); ++i, --r) {
    454     EXPECT_EQ(visits[i].visit_time.ToInternalValue(), url_specifics.visits(r));
    455     EXPECT_EQ(static_cast<const int>(visits[i].transition),
    456               url_specifics.visit_transitions(r));
    457   }
    458 
    459   // Check that in-memory representation of sync state is accurate.
    460   std::set<GURL> sync_state;
    461   typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
    462   EXPECT_FALSE(sync_state.empty());
    463   EXPECT_EQ(1u, sync_state.size());
    464   EXPECT_TRUE(sync_state.end() != sync_state.find(url_row.url()));
    465 }
    466 
    467 TEST_F(TypedUrlSyncableServiceTest, DeleteLocalTypedUrlAndSync) {
    468   syncer::SyncChangeList change_list;
    469 
    470   URLRows url_rows;
    471   std::vector<VisitVector> visit_vectors;
    472   std::vector<const char*> urls;
    473   urls.push_back("http://pie.com/");
    474   urls.push_back("http://cake.com/");
    475   urls.push_back("http://google.com/");
    476   urls.push_back("http://foo.com/");
    477   urls.push_back("http://bar.com/");
    478 
    479   ASSERT_TRUE(
    480       InitiateServerState(4, 1, &url_rows, &visit_vectors, urls, &change_list));
    481   change_list.clear();
    482 
    483   // Check that in-memory representation of sync state is accurate.
    484   std::set<GURL> sync_state;
    485   typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
    486   EXPECT_FALSE(sync_state.empty());
    487   EXPECT_EQ(4u, sync_state.size());
    488 
    489   // Simulate visit expiry of typed visit, no syncing is done
    490   // This is to test that sync relies on the in-memory cache to know
    491   // which urls were typed and synced, and should be deleted.
    492   url_rows[0].set_typed_count(0);
    493   VisitVector visits;
    494   static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
    495       SetVisitsForUrl(url_rows[0].id(), &visits);
    496 
    497   // Delete some urls from backend and create deleted row vector.
    498   URLRows rows;
    499   for (size_t i = 0; i < 3u; ++i) {
    500     static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
    501         DeleteVisitsForUrl(url_rows[i].id());
    502     rows.push_back(url_rows[i]);
    503   }
    504 
    505   // Notify typed url sync service.
    506   typed_url_sync_service_->OnUrlsDeleted(false, false, &rows);
    507 
    508   ASSERT_EQ(3u, change_list.size());
    509   for (size_t i = 0; i < change_list.size(); ++i) {
    510     ASSERT_TRUE(change_list[i].IsValid());
    511     ASSERT_EQ(syncer::TYPED_URLS, change_list[i].sync_data().GetDataType());
    512     EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, change_list[i].change_type());
    513     sync_pb::TypedUrlSpecifics url_specifics =
    514         change_list[i].sync_data().GetSpecifics().typed_url();
    515     EXPECT_EQ(url_rows[i].url().spec(), url_specifics.url());
    516   }
    517 
    518   // Check that in-memory representation of sync state is accurate.
    519   std::set<GURL> sync_state_deleted;
    520   typed_url_sync_service_.get()->GetSyncedUrls(&sync_state_deleted);
    521   ASSERT_EQ(1u, sync_state_deleted.size());
    522   EXPECT_TRUE(sync_state_deleted.end() !=
    523               sync_state_deleted.find(url_rows[3].url()));
    524 }
    525 
    526 TEST_F(TypedUrlSyncableServiceTest, DeleteAllLocalTypedUrlAndSync) {
    527   syncer::SyncChangeList change_list;
    528 
    529   URLRows url_rows;
    530   std::vector<VisitVector> visit_vectors;
    531   std::vector<const char*> urls;
    532   urls.push_back("http://pie.com/");
    533   urls.push_back("http://cake.com/");
    534   urls.push_back("http://google.com/");
    535   urls.push_back("http://foo.com/");
    536   urls.push_back("http://bar.com/");
    537 
    538   ASSERT_TRUE(
    539       InitiateServerState(4, 1, &url_rows, &visit_vectors, urls, &change_list));
    540   change_list.clear();
    541 
    542   // Check that in-memory representation of sync state is accurate.
    543   std::set<GURL> sync_state;
    544   typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
    545   EXPECT_EQ(4u, sync_state.size());
    546 
    547   // Delete urls from backend.
    548   for (size_t i = 0; i < 4u; ++ i) {
    549     static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
    550         DeleteVisitsForUrl(url_rows[i].id());
    551   }
    552   // Delete urls with |all_history| flag set.
    553   bool all_history = true;
    554 
    555   // Notify typed url sync service.
    556   typed_url_sync_service_->OnUrlsDeleted(all_history, false, NULL);
    557 
    558   ASSERT_EQ(4u, change_list.size());
    559   for (size_t i = 0; i < change_list.size(); ++i) {
    560     ASSERT_TRUE(change_list[i].IsValid());
    561     ASSERT_EQ(syncer::TYPED_URLS, change_list[i].sync_data().GetDataType());
    562     EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, change_list[i].change_type());
    563   }
    564   // Check that in-memory representation of sync state is accurate.
    565   std::set<GURL> sync_state_deleted;
    566   typed_url_sync_service_.get()->GetSyncedUrls(&sync_state_deleted);
    567   EXPECT_TRUE(sync_state_deleted.empty());
    568 }
    569 
    570 TEST_F(TypedUrlSyncableServiceTest, MaxVisitLocalTypedUrlAndSync) {
    571   syncer::SyncChangeList change_list;
    572 
    573   URLRows url_rows;
    574   std::vector<VisitVector> visit_vectors;
    575   std::vector<const char*> urls;
    576   urls.push_back("http://pie.com/");
    577 
    578   ASSERT_TRUE(
    579       InitiateServerState(0, 1, &url_rows, &visit_vectors, urls, &change_list));
    580 
    581   URLRow url_row = url_rows.front();
    582   VisitVector visits;
    583 
    584   // Add |kMaxTypedUrlVisits| + 10 visits to the url. The 10 oldest
    585   // non-typed visits are expected to be skipped.
    586   int i = 1;
    587   for (; i <= kMaxTypedUrlVisits - 20; ++i)
    588     AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_TYPED, i);
    589   for (; i <= kMaxTypedUrlVisits; ++i)
    590     AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_LINK, i);
    591   for (; i <= kMaxTypedUrlVisits + 10; ++i)
    592     AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_TYPED, i);
    593 
    594   static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
    595       SetVisitsForUrl(url_row.id(), &visits);
    596 
    597   // Notify typed url sync service of typed visit.
    598   content::PageTransition transition = content::PAGE_TRANSITION_TYPED;
    599   typed_url_sync_service_->OnUrlVisited(transition, &url_row);
    600 
    601   ASSERT_EQ(1u, change_list.size());
    602   ASSERT_TRUE(change_list[0].IsValid());
    603   sync_pb::TypedUrlSpecifics url_specifics =
    604       change_list[0].sync_data().GetSpecifics().typed_url();
    605   ASSERT_EQ(kMaxTypedUrlVisits, url_specifics.visits_size());
    606 
    607   // Check that each visit has been translated/communicated correctly.
    608   // Note that the specifics record visits in chronological order, and the
    609   // visits from the db are in reverse chronological order.
    610   int num_typed_visits_synced = 0;
    611   int num_other_visits_synced = 0;
    612   int r = url_specifics.visits_size() - 1;
    613   for (int i = 0; i < url_specifics.visits_size(); ++i, --r) {
    614     if (url_specifics.visit_transitions(i) == content::PAGE_TRANSITION_TYPED) {
    615       ++num_typed_visits_synced;
    616     } else {
    617       ++num_other_visits_synced;
    618     }
    619   }
    620   EXPECT_EQ(kMaxTypedUrlVisits - 10, num_typed_visits_synced);
    621   EXPECT_EQ(10, num_other_visits_synced);
    622 }
    623 
    624 TEST_F(TypedUrlSyncableServiceTest, ThrottleVisitLocalTypedUrlSync) {
    625   syncer::SyncChangeList change_list;
    626 
    627   URLRows url_rows;
    628   std::vector<VisitVector> visit_vectors;
    629   std::vector<const char*> urls;
    630   urls.push_back("http://pie.com/");
    631 
    632   ASSERT_TRUE(
    633       InitiateServerState(0, 1, &url_rows, &visit_vectors, urls, &change_list));
    634 
    635   URLRow url_row = url_rows.front();
    636   VisitVector visits;
    637 
    638   // Add enough visits to the url so that typed count is above the throttle
    639   // limit, and not right on the interval that gets synced.
    640   for (int i = 1; i < 42; ++i)
    641     AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_TYPED, i);
    642 
    643   static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
    644       SetVisitsForUrl(url_row.id(), &visits);
    645 
    646   // Notify typed url sync service of typed visit.
    647   content::PageTransition transition = content::PAGE_TRANSITION_TYPED;
    648   typed_url_sync_service_->OnUrlVisited(transition, &url_row);
    649 
    650   // Should throttle, so sync and local cache should not update.
    651   ASSERT_EQ(0u, change_list.size());
    652   std::set<GURL> sync_state;
    653   typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
    654   EXPECT_TRUE(sync_state.empty());
    655 }
    656 
    657 }  // namespace history
    658