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.
5 #include "chrome/browser/history/typed_url_syncable_service.h"
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"
20 using history::HistoryBackend;
22 using history::URLRow;
23 using history::URLRows;
24 using history::VisitRow;
25 using history::VisitVector;
29 // Constants used to limit size of visits processed.
30 const int kMaxTypedUrlVisits = 100;
32 // Visits with this timestamp are treated as expired.
33 const int EXPIRED_VISIT = -1;
35 // TestChangeProcessor --------------------------------------------------------
37 class TestChangeProcessor : public syncer::SyncChangeProcessor {
39 TestChangeProcessor() : change_output_(NULL) {}
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(),
47 return syncer::SyncError();
50 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
52 return syncer::SyncDataList();
55 // Set pointer location to write SyncChanges to in ProcessSyncChanges.
56 void SetChangeOutput(syncer::SyncChangeList *change_output) {
57 change_output_ = change_output;
61 syncer::SyncChangeList *change_output_;
63 DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor);
66 // TestHistoryBackend ----------------------------------------------------------
68 class TestHistoryBackend : public HistoryBackend {
70 TestHistoryBackend() : HistoryBackend(base::FilePath(), 0, NULL, NULL) {}
72 // HistoryBackend test implementation.
73 virtual bool IsExpiredVisitTime(const base::Time& time) OVERRIDE {
74 return time.ToInternalValue() == EXPIRED_VISIT;
77 virtual bool GetMostRecentVisitsForURL(
80 VisitVector* visits) OVERRIDE {
81 if (local_db_visits_[id].empty())
84 visits->insert(visits->end(),
85 local_db_visits_[id].begin(),
86 local_db_visits_[id].end());
91 void SetVisitsForUrl(URLID id, VisitVector* visits) {
92 if (!local_db_visits_[id].empty()) {
93 local_db_visits_[id].clear();
96 local_db_visits_[id].insert(local_db_visits_[id].end(),
101 void DeleteVisitsForUrl(const URLID& id) {
102 local_db_visits_.erase(id);
106 virtual ~TestHistoryBackend() {}
108 // Mock of visit table in local db.
109 std::map<URLID, VisitVector> local_db_visits_;
116 // TypedUrlSyncableServiceTest -------------------------------------------------
118 class TypedUrlSyncableServiceTest : public testing::Test {
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,
131 VisitVector* visits);
133 static void AddNewestVisit(URLRow* url,
135 content::PageTransition transition,
138 static void AddOldestVisit(URLRow* url,
140 content::PageTransition transition,
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()));
150 bool InitiateServerState(
151 unsigned int num_typed_urls,
152 unsigned int num_reload_urls,
154 std::vector<VisitVector>* visit_vectors,
155 const std::vector<const char*>& urls,
156 syncer::SyncChangeList* change_list);
159 TypedUrlSyncableServiceTest() {}
161 virtual ~TypedUrlSyncableServiceTest() {}
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);
170 scoped_refptr<HistoryBackend> fake_history_backend_;
171 scoped_ptr<TypedUrlSyncableService> typed_url_sync_service_;
172 scoped_ptr<syncer::SyncChangeProcessor> fake_change_processor_;
175 URLRow TypedUrlSyncableServiceTest::MakeTypedUrlRow(
181 VisitVector* visits) {
182 DCHECK(visits->empty());
184 // Give each URL a unique ID, to mimic the behavior of the real database.
185 static int unique_url_id = 0;
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);
192 base::Time last_visit_time = base::Time::FromInternalValue(last_visit);
193 history_url.set_last_visit(last_visit_time);
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));
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));
208 history_url.set_visit_count(visits->size());
212 void TypedUrlSyncableServiceTest::AddNewestVisit(
215 content::PageTransition transition,
217 base::Time time = base::Time::FromInternalValue(visit_time);
218 visits->insert(visits->begin(),
219 VisitRow(url->id(), time, 0, transition, 0));
221 if (transition == content::PAGE_TRANSITION_TYPED) {
222 url->set_typed_count(url->typed_count() + 1);
225 url->set_last_visit(time);
226 url->set_visit_count(visits->size());
229 void TypedUrlSyncableServiceTest::AddOldestVisit(
232 content::PageTransition transition,
234 base::Time time = base::Time::FromInternalValue(visit_time);
235 visits->push_back(VisitRow(url->id(), time, 0, transition, 0));
237 if (transition == content::PAGE_TRANSITION_TYPED) {
238 url->set_typed_count(url->typed_count() + 1);
241 url->set_visit_count(visits->size());
244 bool TypedUrlSyncableServiceTest::InitiateServerState(
245 unsigned int num_typed_urls,
246 unsigned int num_reload_urls,
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())
256 static_cast<TestChangeProcessor*>(fake_change_processor_.get())->
257 SetChangeOutput(change_list);
259 // Set change processor.
260 syncer::SyncMergeResult result =
261 typed_url_sync_service_->MergeDataAndStartSyncing(
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();
270 // Create new URL rows, populate the mock backend with its visits, and
271 // send to the sync service.
272 URLRows changed_urls;
274 for (unsigned int i = 0; i < total_urls; ++i) {
275 int typed = i < num_typed_urls ? 1 : 0;
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());
285 typed_url_sync_service_->OnUrlsModified(&changed_urls);
287 // Check that communication with sync was successful.
288 if (num_typed_urls != change_list->size())
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;
299 std::vector<VisitVector> visit_vectors;
300 std::vector<const char*> urls;
301 urls.push_back("http://pie.com/");
304 InitiateServerState(1, 0, &url_rows, &visit_vectors, urls, &change_list));
306 URLRow url_row = url_rows.front();
307 VisitVector visits = visit_vectors.front();
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());
315 // Get typed url specifics.
316 sync_pb::TypedUrlSpecifics url_specifics =
317 change_list[0].sync_data().GetSpecifics().typed_url();
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));
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()));
334 TEST_F(TypedUrlSyncableServiceTest, UpdateLocalTypedUrlAndSync) {
335 syncer::SyncChangeList change_list;
338 std::vector<VisitVector> visit_vectors;
339 std::vector<const char*> urls;
340 urls.push_back("http://pie.com/");
343 InitiateServerState(1, 0, &url_rows, &visit_vectors, urls, &change_list));
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();
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);
356 // Notify typed url sync service of the update.
357 typed_url_sync_service_->OnUrlsModified(&changed_urls);
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());
364 sync_pb::TypedUrlSpecifics url_specifics =
365 change_list[0].sync_data().GetSpecifics().typed_url();
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());
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));
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()));
389 TEST_F(TypedUrlSyncableServiceTest, LinkVisitLocalTypedUrlAndSync) {
390 syncer::SyncChangeList change_list;
393 std::vector<VisitVector> visit_vectors;
394 std::vector<const char*> urls;
395 urls.push_back("http://pie.com/");
398 InitiateServerState(1, 0, &url_rows, &visit_vectors, urls, &change_list));
401 URLRow url_row = url_rows.front();
402 VisitVector visits = visit_vectors.front();
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);
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());
415 TEST_F(TypedUrlSyncableServiceTest, TypedVisitLocalTypedUrlAndSync) {
416 syncer::SyncChangeList change_list;
419 std::vector<VisitVector> visit_vectors;
420 std::vector<const char*> urls;
421 urls.push_back("http://pie.com/");
424 InitiateServerState(1, 0, &url_rows, &visit_vectors, urls, &change_list));
427 URLRow url_row = url_rows.front();
428 VisitVector visits = visit_vectors.front();
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);
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);
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());
446 sync_pb::TypedUrlSpecifics url_specifics =
447 change_list[0].sync_data().GetSpecifics().typed_url();
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());
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));
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()));
471 TEST_F(TypedUrlSyncableServiceTest, DeleteLocalTypedUrlAndSync) {
472 syncer::SyncChangeList change_list;
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/");
484 InitiateServerState(4, 1, &url_rows, &visit_vectors, urls, &change_list));
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());
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);
498 static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
499 SetVisitsForUrl(url_rows[0].id(), &visits);
501 // Delete some urls from backend and create deleted row vector.
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]);
509 // Notify typed url sync service.
510 typed_url_sync_service_->OnUrlsDeleted(false, false, &rows);
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());
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()));
530 TEST_F(TypedUrlSyncableServiceTest, DeleteAllLocalTypedUrlAndSync) {
531 syncer::SyncChangeList change_list;
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/");
543 InitiateServerState(4, 1, &url_rows, &visit_vectors, urls, &change_list));
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());
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());
556 // Delete urls with |all_history| flag set.
557 bool all_history = true;
559 // Notify typed url sync service.
560 typed_url_sync_service_->OnUrlsDeleted(all_history, false, NULL);
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());
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());
574 TEST_F(TypedUrlSyncableServiceTest, MaxVisitLocalTypedUrlAndSync) {
575 syncer::SyncChangeList change_list;
578 std::vector<VisitVector> visit_vectors;
579 std::vector<const char*> urls;
580 urls.push_back("http://pie.com/");
583 InitiateServerState(0, 1, &url_rows, &visit_vectors, urls, &change_list));
585 URLRow url_row = url_rows.front();
588 // Add |kMaxTypedUrlVisits| + 10 visits to the url. The 10 oldest
589 // non-typed visits are expected to be skipped.
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);
598 static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
599 SetVisitsForUrl(url_row.id(), &visits);
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);
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());
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;
621 ++num_other_visits_synced;
624 EXPECT_EQ(kMaxTypedUrlVisits - 10, num_typed_visits_synced);
625 EXPECT_EQ(10, num_other_visits_synced);
628 TEST_F(TypedUrlSyncableServiceTest, ThrottleVisitLocalTypedUrlSync) {
629 syncer::SyncChangeList change_list;
632 std::vector<VisitVector> visit_vectors;
633 std::vector<const char*> urls;
634 urls.push_back("http://pie.com/");
637 InitiateServerState(0, 1, &url_rows, &visit_vectors, urls, &change_list));
639 URLRow url_row = url_rows.front();
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);
647 static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
648 SetVisitsForUrl(url_row.id(), &visits);
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);
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());
661 } // namespace history