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