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