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