- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / history / typed_url_syncable_service_unittest.cc
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