1 // Copyright (c) 2012 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.
7 #include "testing/gtest/include/gtest/gtest.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/location.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/thread.h"
17 #include "base/time/time.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/history/history_backend.h"
20 #include "chrome/browser/history/history_db_task.h"
21 #include "chrome/browser/history/history_notifications.h"
22 #include "chrome/browser/history/history_service.h"
23 #include "chrome/browser/history/history_service_factory.h"
24 #include "chrome/browser/history/history_types.h"
25 #include "chrome/browser/invalidation/invalidation_service_factory.h"
26 #include "chrome/browser/signin/signin_manager.h"
27 #include "chrome/browser/signin/signin_manager_factory.h"
28 #include "chrome/browser/signin/token_service_factory.h"
29 #include "chrome/browser/sync/abstract_profile_sync_service_test.h"
30 #include "chrome/browser/sync/glue/data_type_error_handler_mock.h"
31 #include "chrome/browser/sync/glue/sync_backend_host.h"
32 #include "chrome/browser/sync/glue/typed_url_change_processor.h"
33 #include "chrome/browser/sync/glue/typed_url_data_type_controller.h"
34 #include "chrome/browser/sync/glue/typed_url_model_associator.h"
35 #include "chrome/browser/sync/profile_sync_components_factory.h"
36 #include "chrome/browser/sync/profile_sync_components_factory_mock.h"
37 #include "chrome/browser/sync/profile_sync_service.h"
38 #include "chrome/browser/sync/profile_sync_service_factory.h"
39 #include "chrome/browser/sync/profile_sync_test_util.h"
40 #include "chrome/browser/sync/test_profile_sync_service.h"
41 #include "chrome/test/base/testing_profile.h"
42 #include "components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h"
43 #include "content/public/browser/notification_service.h"
44 #include "google_apis/gaia/gaia_constants.h"
45 #include "sync/internal_api/public/read_node.h"
46 #include "sync/internal_api/public/read_transaction.h"
47 #include "sync/internal_api/public/write_node.h"
48 #include "sync/internal_api/public/write_transaction.h"
49 #include "sync/protocol/typed_url_specifics.pb.h"
50 #include "testing/gmock/include/gmock/gmock.h"
55 using browser_sync::TypedUrlChangeProcessor;
56 using browser_sync::TypedUrlDataTypeController;
57 using browser_sync::TypedUrlModelAssociator;
58 using history::HistoryBackend;
60 using history::URLRow;
61 using syncer::syncable::WriteTransaction;
64 using testing::Return;
65 using testing::SetArgumentPointee;
68 // Visits with this timestamp are treated as expired.
69 static const int EXPIRED_VISIT = -1;
71 class HistoryBackendMock : public HistoryBackend {
73 HistoryBackendMock() : HistoryBackend(base::FilePath(), 0, NULL, NULL) {}
74 virtual bool IsExpiredVisitTime(const base::Time& time) OVERRIDE {
75 return time.ToInternalValue() == EXPIRED_VISIT;
77 MOCK_METHOD1(GetAllTypedURLs, bool(history::URLRows* entries));
78 MOCK_METHOD3(GetMostRecentVisitsForURL, bool(history::URLID id,
80 history::VisitVector* visits));
81 MOCK_METHOD2(UpdateURL, bool(history::URLID id, const history::URLRow& url));
82 MOCK_METHOD3(AddVisits, bool(const GURL& url,
83 const std::vector<history::VisitInfo>& visits,
84 history::VisitSource visit_source));
85 MOCK_METHOD1(RemoveVisits, bool(const history::VisitVector& visits));
86 MOCK_METHOD2(GetURL, bool(const GURL& url_id, history::URLRow* url_row));
87 MOCK_METHOD2(SetPageTitle, void(const GURL& url, const string16& title));
88 MOCK_METHOD1(DeleteURL, void(const GURL& url));
91 virtual ~HistoryBackendMock() {}
94 class HistoryServiceMock : public HistoryService {
96 explicit HistoryServiceMock(Profile* profile) : HistoryService(profile) {}
97 MOCK_METHOD2(ScheduleDBTask, void(history::HistoryDBTask*,
98 CancelableRequestConsumerBase*));
99 MOCK_METHOD0(Shutdown, void());
101 void ShutdownBaseService() {
102 HistoryService::Shutdown();
106 virtual ~HistoryServiceMock() {}
109 BrowserContextKeyedService* BuildHistoryService(
110 content::BrowserContext* profile) {
111 return new HistoryServiceMock(static_cast<Profile*>(profile));
114 class TestTypedUrlModelAssociator : public TypedUrlModelAssociator {
116 TestTypedUrlModelAssociator(
117 ProfileSyncService* sync_service,
118 history::HistoryBackend* history_backend,
119 browser_sync::DataTypeErrorHandler* error_handler) :
120 TypedUrlModelAssociator(sync_service, history_backend, error_handler) {}
123 // Don't clear error stats - that way we can verify their values in our
125 virtual void ClearErrorStats() OVERRIDE {}
128 void RunOnDBThreadCallback(HistoryBackend* backend,
129 history::HistoryDBTask* task) {
130 task->RunOnDBThread(backend, NULL);
133 ACTION_P2(RunTaskOnDBThread, thread, backend) {
134 // ScheduleDBTask takes ownership of its task argument, so we
136 scoped_refptr<history::HistoryDBTask> task(arg0);
137 thread->message_loop()->PostTask(
138 FROM_HERE, base::Bind(&RunOnDBThreadCallback, base::Unretained(backend),
142 ACTION_P2(ShutdownHistoryService, thread, service) {
143 service->ShutdownBaseService();
147 ACTION_P6(MakeTypedUrlSyncComponents,
155 new TestTypedUrlModelAssociator(service, hb, error_handler);
156 TypedUrlChangeProcessor* change_processor =
157 new TypedUrlChangeProcessor(profile, *model_associator, hb, dtc);
158 return ProfileSyncComponentsFactory::SyncComponents(*model_associator,
162 class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest {
164 void AddTypedUrlSyncNode(const history::URLRow& url,
165 const history::VisitVector& visits) {
166 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
167 syncer::ReadNode typed_url_root(&trans);
168 ASSERT_EQ(syncer::BaseNode::INIT_OK,
169 typed_url_root.InitByTagLookup(browser_sync::kTypedUrlTag));
171 syncer::WriteNode node(&trans);
172 std::string tag = url.url().spec();
173 syncer::WriteNode::InitUniqueByCreationResult result =
174 node.InitUniqueByCreation(syncer::TYPED_URLS, typed_url_root, tag);
175 ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result);
176 TypedUrlModelAssociator::WriteToSyncNode(url, visits, &node);
180 ProfileSyncServiceTypedUrlTest() {
181 history_thread_.reset(new Thread("history"));
184 virtual void SetUp() {
185 AbstractProfileSyncServiceTest::SetUp();
186 TestingProfile::Builder builder;
187 builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(),
188 FakeOAuth2TokenService::BuildTokenService);
189 profile_ = builder.Build().Pass();
190 invalidation::InvalidationServiceFactory::GetInstance()->
191 SetBuildOnlyFakeInvalidatorsForTest(true);
192 history_backend_ = new HistoryBackendMock();
193 history_service_ = static_cast<HistoryServiceMock*>(
194 HistoryServiceFactory::GetInstance()->SetTestingFactoryAndUse(
195 profile_.get(), BuildHistoryService));
196 EXPECT_CALL((*history_service_), ScheduleDBTask(_, _))
197 .WillRepeatedly(RunTaskOnDBThread(history_thread_.get(),
198 history_backend_.get()));
199 history_thread_->Start();
202 virtual void TearDown() {
203 EXPECT_CALL((*history_service_), Shutdown())
204 .WillOnce(ShutdownHistoryService(history_thread_.release(),
207 AbstractProfileSyncServiceTest::TearDown();
210 TypedUrlModelAssociator* StartSyncService(const base::Closure& callback) {
211 TypedUrlModelAssociator* model_associator = NULL;
212 if (!sync_service_) {
213 SigninManagerBase* signin =
214 SigninManagerFactory::GetForProfile(profile_.get());
215 signin->SetAuthenticatedUsername("test");
216 token_service_ = static_cast<TokenService*>(
217 TokenServiceFactory::GetInstance()->SetTestingFactoryAndUse(
218 profile_.get(), BuildTokenService));
219 sync_service_ = static_cast<TestProfileSyncService*>(
220 ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
222 &TestProfileSyncService::BuildAutoStartAsyncInit));
223 sync_service_->set_backend_init_callback(callback);
224 ProfileSyncComponentsFactoryMock* components =
225 sync_service_->components_factory_mock();
226 TypedUrlDataTypeController* data_type_controller =
227 new TypedUrlDataTypeController(components,
231 EXPECT_CALL(*components, CreateTypedUrlSyncComponents(_, _, _)).
232 WillOnce(MakeTypedUrlSyncComponents(profile_.get(),
234 history_backend_.get(),
235 data_type_controller,
238 EXPECT_CALL(*components, CreateDataTypeManager(_, _, _, _, _, _)).
239 WillOnce(ReturnNewDataTypeManager());
241 ProfileOAuth2TokenService* oauth2_token_service =
242 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_.get());
243 oauth2_token_service->UpdateCredentials("test", "oauth2_login_token");
245 sync_service_->RegisterDataTypeController(data_type_controller);
247 sync_service_->Initialize();
248 base::MessageLoop::current()->Run();
250 return model_associator;
253 void GetTypedUrlsFromSyncDB(history::URLRows* urls) {
255 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
256 syncer::ReadNode typed_url_root(&trans);
257 if (typed_url_root.InitByTagLookup(browser_sync::kTypedUrlTag) !=
258 syncer::BaseNode::INIT_OK)
261 int64 child_id = typed_url_root.GetFirstChildId();
262 while (child_id != syncer::kInvalidId) {
263 syncer::ReadNode child_node(&trans);
264 if (child_node.InitByIdLookup(child_id) != syncer::BaseNode::INIT_OK)
267 const sync_pb::TypedUrlSpecifics& typed_url(
268 child_node.GetTypedUrlSpecifics());
269 history::URLRow new_url(GURL(typed_url.url()));
271 new_url.set_title(UTF8ToUTF16(typed_url.title()));
272 DCHECK(typed_url.visits_size());
273 DCHECK_EQ(typed_url.visits_size(), typed_url.visit_transitions_size());
274 new_url.set_last_visit(base::Time::FromInternalValue(
275 typed_url.visits(typed_url.visits_size() - 1)));
276 new_url.set_hidden(typed_url.hidden());
278 urls->push_back(new_url);
279 child_id = child_node.GetSuccessorId();
283 void SetIdleChangeProcessorExpectations() {
284 EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).Times(0);
285 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).Times(0);
286 EXPECT_CALL((*history_backend_.get()), GetURL(_, _)).Times(0);
287 EXPECT_CALL((*history_backend_.get()), DeleteURL(_)).Times(0);
290 static bool URLsEqual(history::URLRow& lhs, history::URLRow& rhs) {
291 // Only verify the fields we explicitly sync (i.e. don't verify typed_count
292 // or visit_count because we rely on the history DB to manage those values
293 // and they are left unchanged by HistoryBackendMock).
294 return (lhs.url().spec().compare(rhs.url().spec()) == 0) &&
295 (lhs.title().compare(rhs.title()) == 0) &&
296 (lhs.last_visit() == rhs.last_visit()) &&
297 (lhs.hidden() == rhs.hidden());
300 static history::URLRow MakeTypedUrlEntry(const char* url,
305 history::VisitVector* visits) {
306 // Give each URL a unique ID, to mimic the behavior of the real database.
307 static int unique_url_id = 0;
309 URLRow history_url(gurl, ++unique_url_id);
310 history_url.set_title(UTF8ToUTF16(title));
311 history_url.set_typed_count(typed_count);
312 history_url.set_last_visit(
313 base::Time::FromInternalValue(last_visit));
314 history_url.set_hidden(hidden);
315 visits->push_back(history::VisitRow(
316 history_url.id(), history_url.last_visit(), 0,
317 content::PAGE_TRANSITION_TYPED, 0));
318 history_url.set_visit_count(visits->size());
322 scoped_ptr<Thread> history_thread_;
324 scoped_ptr<TestingProfile> profile_;
325 scoped_refptr<HistoryBackendMock> history_backend_;
326 HistoryServiceMock* history_service_;
327 browser_sync::DataTypeErrorHandlerMock error_handler_;
330 void AddTypedUrlEntries(ProfileSyncServiceTypedUrlTest* test,
331 const history::URLRows& entries) {
332 test->CreateRoot(syncer::TYPED_URLS);
333 for (size_t i = 0; i < entries.size(); ++i) {
334 history::VisitVector visits;
335 visits.push_back(history::VisitRow(
336 entries[i].id(), entries[i].last_visit(), 0,
337 content::PageTransitionFromInt(0), 0));
338 test->AddTypedUrlSyncNode(entries[i], visits);
344 TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeEmptySync) {
345 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
346 WillOnce(Return(true));
347 SetIdleChangeProcessorExpectations();
348 CreateRootHelper create_root(this, syncer::TYPED_URLS);
349 TypedUrlModelAssociator* associator =
350 StartSyncService(create_root.callback());
351 history::URLRows sync_entries;
352 GetTypedUrlsFromSyncDB(&sync_entries);
353 EXPECT_EQ(0U, sync_entries.size());
354 ASSERT_EQ(0, associator->GetErrorPercentage());
357 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeEmptySync) {
358 history::URLRows entries;
359 history::VisitVector visits;
360 entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar",
361 2, 15, false, &visits));
363 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
364 WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
365 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
366 WillRepeatedly(DoAll(SetArgumentPointee<2>(visits), Return(true)));
367 SetIdleChangeProcessorExpectations();
368 CreateRootHelper create_root(this, syncer::TYPED_URLS);
369 TypedUrlModelAssociator* associator =
370 StartSyncService(create_root.callback());
371 history::URLRows sync_entries;
372 GetTypedUrlsFromSyncDB(&sync_entries);
373 ASSERT_EQ(1U, sync_entries.size());
374 EXPECT_TRUE(URLsEqual(entries[0], sync_entries[0]));
375 ASSERT_EQ(0, associator->GetErrorPercentage());
378 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeErrorReadingVisits) {
379 history::URLRows entries;
380 history::VisitVector visits;
381 history::URLRow native_entry1(MakeTypedUrlEntry("http://foo.com", "bar",
382 2, 15, false, &visits));
383 history::URLRow native_entry2(MakeTypedUrlEntry("http://foo2.com", "bar",
384 3, 15, false, &visits));
385 entries.push_back(native_entry1);
386 entries.push_back(native_entry2);
387 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
388 WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
389 // Return an error from GetMostRecentVisitsForURL() for the second URL.
390 EXPECT_CALL((*history_backend_.get()),
391 GetMostRecentVisitsForURL(native_entry1.id(), _, _)).
392 WillRepeatedly(Return(true));
393 EXPECT_CALL((*history_backend_.get()),
394 GetMostRecentVisitsForURL(native_entry2.id(), _, _)).
395 WillRepeatedly(Return(false));
396 SetIdleChangeProcessorExpectations();
397 CreateRootHelper create_root(this, syncer::TYPED_URLS);
398 StartSyncService(create_root.callback());
399 history::URLRows sync_entries;
400 GetTypedUrlsFromSyncDB(&sync_entries);
401 ASSERT_EQ(1U, sync_entries.size());
402 EXPECT_TRUE(URLsEqual(native_entry1, sync_entries[0]));
405 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeWithBlankEmptySync) {
406 std::vector<history::URLRow> entries;
407 history::VisitVector visits;
409 entries.push_back(MakeTypedUrlEntry("", "bar",
410 2, 15, false, &visits));
411 entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar",
412 2, 15, false, &visits));
413 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
414 WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
415 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
416 WillRepeatedly(DoAll(SetArgumentPointee<2>(visits), Return(true)));
417 SetIdleChangeProcessorExpectations();
418 CreateRootHelper create_root(this, syncer::TYPED_URLS);
419 StartSyncService(create_root.callback());
420 std::vector<history::URLRow> sync_entries;
421 GetTypedUrlsFromSyncDB(&sync_entries);
422 // The empty URL should be ignored.
423 ASSERT_EQ(1U, sync_entries.size());
424 EXPECT_TRUE(URLsEqual(entries[1], sync_entries[0]));
427 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncNoMerge) {
428 history::VisitVector native_visits;
429 history::VisitVector sync_visits;
430 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry",
431 2, 15, false, &native_visits));
432 history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry",
433 3, 16, false, &sync_visits));
435 history::URLRows native_entries;
436 native_entries.push_back(native_entry);
437 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
438 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
439 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
440 WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true)));
441 EXPECT_CALL((*history_backend_.get()),
442 AddVisits(_, _, history::SOURCE_SYNCED)).WillRepeatedly(Return(true));
444 history::URLRows sync_entries;
445 sync_entries.push_back(sync_entry);
447 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).
448 WillRepeatedly(Return(true));
449 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
451 std::map<std::string, history::URLRow> expected;
452 expected[native_entry.url().spec()] = native_entry;
453 expected[sync_entry.url().spec()] = sync_entry;
455 history::URLRows new_sync_entries;
456 GetTypedUrlsFromSyncDB(&new_sync_entries);
458 EXPECT_TRUE(new_sync_entries.size() == expected.size());
459 for (history::URLRows::iterator entry = new_sync_entries.begin();
460 entry != new_sync_entries.end(); ++entry) {
461 EXPECT_TRUE(URLsEqual(expected[entry->url().spec()], *entry));
465 TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeExpiredSync) {
466 history::VisitVector sync_visits;
467 history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry",
468 3, EXPIRED_VISIT, false,
470 history::URLRows sync_entries;
471 sync_entries.push_back(sync_entry);
473 // Since all our URLs are expired, no backend calls to add new URLs will be
475 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
476 WillOnce(Return(true));
477 SetIdleChangeProcessorExpectations();
479 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
482 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncMerge) {
483 history::VisitVector native_visits;
484 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry",
485 2, 15, false, &native_visits));
486 history::VisitVector sync_visits;
487 history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "name",
488 1, 17, false, &sync_visits));
489 history::VisitVector merged_visits;
490 merged_visits.push_back(history::VisitRow(
491 sync_entry.id(), base::Time::FromInternalValue(15), 0,
492 content::PageTransitionFromInt(0), 0));
494 history::URLRow merged_entry(MakeTypedUrlEntry("http://native.com", "name",
495 2, 17, false, &merged_visits));
497 history::URLRows native_entries;
498 native_entries.push_back(native_entry);
499 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
500 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
501 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
502 WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true)));
503 EXPECT_CALL((*history_backend_.get()),
504 AddVisits(_, _, history::SOURCE_SYNCED)). WillRepeatedly(Return(true));
506 history::URLRows sync_entries;
507 sync_entries.push_back(sync_entry);
509 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).
510 WillRepeatedly(Return(true));
511 EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).
512 WillRepeatedly(Return());
513 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
515 history::URLRows new_sync_entries;
516 GetTypedUrlsFromSyncDB(&new_sync_entries);
517 ASSERT_EQ(1U, new_sync_entries.size());
518 EXPECT_TRUE(URLsEqual(merged_entry, new_sync_entries[0]));
521 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeWithErrorHasSyncMerge) {
522 history::VisitVector native_visits;
523 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "native",
524 2, 15, false, &native_visits));
525 history::VisitVector sync_visits;
526 history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "sync",
527 1, 17, false, &sync_visits));
529 history::URLRows native_entries;
530 native_entries.push_back(native_entry);
531 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
532 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
533 // Return an error getting the visits for the native URL.
534 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
535 WillRepeatedly(Return(false));
536 EXPECT_CALL((*history_backend_.get()), GetURL(_, _)).
537 WillRepeatedly(DoAll(SetArgumentPointee<1>(native_entry), Return(true)));
538 EXPECT_CALL((*history_backend_.get()),
539 AddVisits(_, _, history::SOURCE_SYNCED)). WillRepeatedly(Return(true));
541 history::URLRows sync_entries;
542 sync_entries.push_back(sync_entry);
544 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).
545 WillRepeatedly(Return(true));
546 EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).
547 WillRepeatedly(Return());
548 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
550 history::URLRows new_sync_entries;
551 GetTypedUrlsFromSyncDB(&new_sync_entries);
552 ASSERT_EQ(1U, new_sync_entries.size());
553 EXPECT_TRUE(URLsEqual(sync_entry, new_sync_entries[0]));
556 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAdd) {
557 history::VisitVector added_visits;
558 history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry",
559 2, 15, false, &added_visits));
561 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
562 WillOnce(Return(true));
563 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
564 WillOnce(DoAll(SetArgumentPointee<2>(added_visits), Return(true)));
566 SetIdleChangeProcessorExpectations();
567 CreateRootHelper create_root(this, syncer::TYPED_URLS);
568 StartSyncService(create_root.callback());
570 history::URLsModifiedDetails details;
571 details.changed_urls.push_back(added_entry);
572 scoped_refptr<ThreadNotifier> notifier(
573 new ThreadNotifier(history_thread_.get()));
574 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
575 content::Source<Profile>(profile_.get()),
576 content::Details<history::URLsModifiedDetails>(&details));
578 history::URLRows new_sync_entries;
579 GetTypedUrlsFromSyncDB(&new_sync_entries);
580 ASSERT_EQ(1U, new_sync_entries.size());
581 EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0]));
584 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAddWithBlank) {
585 history::VisitVector added_visits;
586 history::URLRow empty_entry(MakeTypedUrlEntry("", "entry",
587 2, 15, false, &added_visits));
588 history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry",
589 2, 15, false, &added_visits));
591 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
592 WillOnce(Return(true));
593 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
594 WillRepeatedly(DoAll(SetArgumentPointee<2>(added_visits), Return(true)));
596 SetIdleChangeProcessorExpectations();
597 CreateRootHelper create_root(this, syncer::TYPED_URLS);
598 StartSyncService(create_root.callback());
600 history::URLsModifiedDetails details;
601 details.changed_urls.push_back(empty_entry);
602 details.changed_urls.push_back(added_entry);
603 scoped_refptr<ThreadNotifier> notifier(
604 new ThreadNotifier(history_thread_.get()));
605 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
606 content::Source<Profile>(profile_.get()),
607 content::Details<history::URLsModifiedDetails>(&details));
609 std::vector<history::URLRow> new_sync_entries;
610 GetTypedUrlsFromSyncDB(&new_sync_entries);
611 ASSERT_EQ(1U, new_sync_entries.size());
612 EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0]));
615 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdate) {
616 history::VisitVector original_visits;
617 history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry",
620 history::URLRows original_entries;
621 original_entries.push_back(original_entry);
623 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
624 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
625 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
626 WillOnce(DoAll(SetArgumentPointee<2>(original_visits),
628 CreateRootHelper create_root(this, syncer::TYPED_URLS);
629 StartSyncService(create_root.callback());
631 history::VisitVector updated_visits;
632 history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry",
635 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
636 WillOnce(DoAll(SetArgumentPointee<2>(updated_visits),
639 history::URLsModifiedDetails details;
640 details.changed_urls.push_back(updated_entry);
641 scoped_refptr<ThreadNotifier> notifier(
642 new ThreadNotifier(history_thread_.get()));
643 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
644 content::Source<Profile>(profile_.get()),
645 content::Details<history::URLsModifiedDetails>(&details));
647 history::URLRows new_sync_entries;
648 GetTypedUrlsFromSyncDB(&new_sync_entries);
649 ASSERT_EQ(1U, new_sync_entries.size());
650 EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0]));
653 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAddFromVisit) {
654 history::VisitVector added_visits;
655 history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry",
656 2, 15, false, &added_visits));
658 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
659 WillOnce(Return(true));
660 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
661 WillOnce(DoAll(SetArgumentPointee<2>(added_visits), Return(true)));
663 SetIdleChangeProcessorExpectations();
664 CreateRootHelper create_root(this, syncer::TYPED_URLS);
665 StartSyncService(create_root.callback());
667 history::URLVisitedDetails details;
668 details.row = added_entry;
669 details.transition = content::PAGE_TRANSITION_TYPED;
670 scoped_refptr<ThreadNotifier> notifier(
671 new ThreadNotifier(history_thread_.get()));
672 notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
673 content::Source<Profile>(profile_.get()),
674 content::Details<history::URLVisitedDetails>(&details));
676 history::URLRows new_sync_entries;
677 GetTypedUrlsFromSyncDB(&new_sync_entries);
678 ASSERT_EQ(1U, new_sync_entries.size());
679 EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0]));
682 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdateFromVisit) {
683 history::VisitVector original_visits;
684 history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry",
687 history::URLRows original_entries;
688 original_entries.push_back(original_entry);
690 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
691 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
692 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
693 WillOnce(DoAll(SetArgumentPointee<2>(original_visits),
695 CreateRootHelper create_root(this, syncer::TYPED_URLS);
696 StartSyncService(create_root.callback());
698 history::VisitVector updated_visits;
699 history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry",
702 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
703 WillOnce(DoAll(SetArgumentPointee<2>(updated_visits),
706 history::URLVisitedDetails details;
707 details.row = updated_entry;
708 details.transition = content::PAGE_TRANSITION_TYPED;
709 scoped_refptr<ThreadNotifier> notifier(
710 new ThreadNotifier(history_thread_.get()));
711 notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
712 content::Source<Profile>(profile_.get()),
713 content::Details<history::URLVisitedDetails>(&details));
715 history::URLRows new_sync_entries;
716 GetTypedUrlsFromSyncDB(&new_sync_entries);
717 ASSERT_EQ(1U, new_sync_entries.size());
718 EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0]));
721 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserIgnoreChangeUpdateFromVisit) {
722 history::VisitVector original_visits;
723 history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry",
726 history::URLRows original_entries;
727 original_entries.push_back(original_entry);
729 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
730 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
731 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
732 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits),
734 CreateRootHelper create_root(this, syncer::TYPED_URLS);
735 StartSyncService(create_root.callback());
736 history::URLRows new_sync_entries;
737 GetTypedUrlsFromSyncDB(&new_sync_entries);
738 ASSERT_EQ(1U, new_sync_entries.size());
739 EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0]));
741 history::VisitVector updated_visits;
742 history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry",
745 history::URLVisitedDetails details;
746 details.row = updated_entry;
748 // Should ignore this change because it's not TYPED.
749 details.transition = content::PAGE_TRANSITION_RELOAD;
750 scoped_refptr<ThreadNotifier> notifier(
751 new ThreadNotifier(history_thread_.get()));
752 notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
753 content::Source<Profile>(profile_.get()),
754 content::Details<history::URLVisitedDetails>(&details));
756 GetTypedUrlsFromSyncDB(&new_sync_entries);
758 // Should be no changes to the sync DB from this notification.
759 ASSERT_EQ(1U, new_sync_entries.size());
760 EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0]));
762 // Now, try updating it with a large number of visits not divisible by 10
763 // (should ignore this visit).
764 history::URLRow twelve_visits(MakeTypedUrlEntry("http://mine.com", "entry",
767 details.row = twelve_visits;
768 details.transition = content::PAGE_TRANSITION_TYPED;
769 notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
770 content::Source<Profile>(profile_.get()),
771 content::Details<history::URLVisitedDetails>(&details));
772 GetTypedUrlsFromSyncDB(&new_sync_entries);
773 // Should be no changes to the sync DB from this notification.
774 ASSERT_EQ(1U, new_sync_entries.size());
775 EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0]));
777 // Now, try updating it with a large number of visits that is divisible by 10
778 // (should *not* be ignored).
779 history::URLRow twenty_visits(MakeTypedUrlEntry("http://mine.com", "entry",
782 details.row = twenty_visits;
783 details.transition = content::PAGE_TRANSITION_TYPED;
784 notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
785 content::Source<Profile>(profile_.get()),
786 content::Details<history::URLVisitedDetails>(&details));
787 GetTypedUrlsFromSyncDB(&new_sync_entries);
788 ASSERT_EQ(1U, new_sync_entries.size());
789 EXPECT_TRUE(URLsEqual(twenty_visits, new_sync_entries[0]));
792 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemove) {
793 history::VisitVector original_visits1;
794 history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry",
797 history::VisitVector original_visits2;
798 history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com",
802 history::URLRows original_entries;
803 original_entries.push_back(original_entry1);
804 original_entries.push_back(original_entry2);
806 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
807 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
808 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
809 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1),
811 CreateRootHelper create_root(this, syncer::TYPED_URLS);
812 StartSyncService(create_root.callback());
814 history::URLsDeletedDetails changes;
815 changes.all_history = false;
816 changes.rows.push_back(history::URLRow(GURL("http://mine.com")));
817 scoped_refptr<ThreadNotifier> notifier(
818 new ThreadNotifier(history_thread_.get()));
819 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
820 content::Source<Profile>(profile_.get()),
821 content::Details<history::URLsDeletedDetails>(&changes));
823 history::URLRows new_sync_entries;
824 GetTypedUrlsFromSyncDB(&new_sync_entries);
825 ASSERT_EQ(1U, new_sync_entries.size());
826 EXPECT_TRUE(URLsEqual(original_entry2, new_sync_entries[0]));
829 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveArchive) {
830 history::VisitVector original_visits1;
831 history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry",
834 history::VisitVector original_visits2;
835 history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com",
839 history::URLRows original_entries;
840 original_entries.push_back(original_entry1);
841 original_entries.push_back(original_entry2);
843 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
844 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
845 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
846 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1),
848 CreateRootHelper create_root(this, syncer::TYPED_URLS);
849 StartSyncService(create_root.callback());
851 history::URLsDeletedDetails changes;
852 changes.all_history = false;
853 // Setting archived=true should cause the sync code to ignore this deletion.
854 changes.archived = true;
855 changes.rows.push_back(history::URLRow(GURL("http://mine.com")));
856 scoped_refptr<ThreadNotifier> notifier(
857 new ThreadNotifier(history_thread_.get()));
858 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
859 content::Source<Profile>(profile_.get()),
860 content::Details<history::URLsDeletedDetails>(&changes));
862 history::URLRows new_sync_entries;
863 GetTypedUrlsFromSyncDB(&new_sync_entries);
864 // Both URLs should still be there.
865 ASSERT_EQ(2U, new_sync_entries.size());
868 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveAll) {
869 history::VisitVector original_visits1;
870 history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry",
873 history::VisitVector original_visits2;
874 history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com",
878 history::URLRows original_entries;
879 original_entries.push_back(original_entry1);
880 original_entries.push_back(original_entry2);
882 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
883 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
884 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
885 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1),
887 CreateRootHelper create_root(this, syncer::TYPED_URLS);
888 StartSyncService(create_root.callback());
890 history::URLRows new_sync_entries;
891 GetTypedUrlsFromSyncDB(&new_sync_entries);
892 ASSERT_EQ(2U, new_sync_entries.size());
894 history::URLsDeletedDetails changes;
895 changes.all_history = true;
896 scoped_refptr<ThreadNotifier> notifier(
897 new ThreadNotifier(history_thread_.get()));
898 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
899 content::Source<Profile>(profile_.get()),
900 content::Details<history::URLsDeletedDetails>(&changes));
902 GetTypedUrlsFromSyncDB(&new_sync_entries);
903 ASSERT_EQ(0U, new_sync_entries.size());
906 TEST_F(ProfileSyncServiceTypedUrlTest, FailWriteToHistoryBackend) {
907 history::VisitVector native_visits;
908 history::VisitVector sync_visits;
909 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry",
910 2, 15, false, &native_visits));
911 history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry",
912 3, 16, false, &sync_visits));
914 history::URLRows native_entries;
915 native_entries.push_back(native_entry);
916 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
917 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
918 EXPECT_CALL((*history_backend_.get()), GetURL(_, _)).
919 WillOnce(DoAll(SetArgumentPointee<1>(native_entry), Return(true)));
920 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
921 WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true)));
922 EXPECT_CALL((*history_backend_.get()),
923 AddVisits(_, _, history::SOURCE_SYNCED)).WillRepeatedly(Return(false));
925 history::URLRows sync_entries;
926 sync_entries.push_back(sync_entry);
928 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).
929 WillRepeatedly(Return(false));
930 TypedUrlModelAssociator* associator =
931 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
932 // Errors writing to the DB should be recorded, but should not cause an
933 // unrecoverable error.
935 sync_service_->failed_data_types_handler().GetFailedTypes().Has(
936 syncer::TYPED_URLS));
937 // Some calls should have succeeded, so the error percentage should be
938 // somewhere > 0 and < 100.
939 ASSERT_NE(0, associator->GetErrorPercentage());
940 ASSERT_NE(100, associator->GetErrorPercentage());
943 TEST_F(ProfileSyncServiceTypedUrlTest, FailToGetTypedURLs) {
944 history::VisitVector native_visits;
945 history::VisitVector sync_visits;
946 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry",
947 2, 15, false, &native_visits));
948 history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry",
949 3, 16, false, &sync_visits));
951 history::URLRows native_entries;
952 native_entries.push_back(native_entry);
953 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
954 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(false)));
956 history::URLRows sync_entries;
957 sync_entries.push_back(sync_entry);
959 EXPECT_CALL(error_handler_, CreateAndUploadError(_, _, _)).
960 WillOnce(Return(syncer::SyncError(
962 syncer::SyncError::DATATYPE_ERROR,
964 syncer::TYPED_URLS)));
965 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries));
966 // Errors getting typed URLs will cause an unrecoverable error (since we can
967 // do *nothing* in that case).
969 sync_service_->failed_data_types_handler().GetFailedTypes().Has(
970 syncer::TYPED_URLS));
972 1u, sync_service_->failed_data_types_handler().GetFailedTypes().Size());
973 // Can't check GetErrorPercentage(), because generating an unrecoverable
974 // error will free the model associator.
977 TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalFileURL) {
978 history::VisitVector original_visits;
979 // Create http and file url.
980 history::URLRow url_entry(MakeTypedUrlEntry("http://yey.com",
981 "yey", 12, 15, false,
983 history::URLRow file_entry(MakeTypedUrlEntry("file:///kitty.jpg",
984 "kitteh", 12, 15, false,
987 history::URLRows original_entries;
988 original_entries.push_back(url_entry);
989 original_entries.push_back(file_entry);
991 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
992 WillRepeatedly(DoAll(SetArgumentPointee<0>(original_entries),
994 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
995 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits),
997 CreateRootHelper create_root(this, syncer::TYPED_URLS);
998 StartSyncService(create_root.callback());
1000 history::VisitVector updated_visits;
1001 // Create updates for the previous urls + a new file one.
1002 history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com",
1003 "yey", 20, 15, false,
1005 history::URLRow updated_file_entry(MakeTypedUrlEntry("file:///cat.jpg",
1006 "cat", 20, 15, false,
1008 history::URLRow new_file_entry(MakeTypedUrlEntry("file:///dog.jpg",
1009 "dog", 20, 15, false,
1011 history::URLsModifiedDetails details;
1012 details.changed_urls.push_back(updated_url_entry);
1013 details.changed_urls.push_back(updated_file_entry);
1014 details.changed_urls.push_back(new_file_entry);
1015 scoped_refptr<ThreadNotifier> notifier(
1016 new ThreadNotifier(history_thread_.get()));
1017 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
1018 content::Source<Profile>(profile_.get()),
1019 content::Details<history::URLsModifiedDetails>(&details));
1021 history::URLRows new_sync_entries;
1022 GetTypedUrlsFromSyncDB(&new_sync_entries);
1024 // We should ignore the local file urls (existing and updated),
1025 // and only be left with the updated http url.
1026 ASSERT_EQ(1U, new_sync_entries.size());
1027 EXPECT_TRUE(URLsEqual(updated_url_entry, new_sync_entries[0]));
1030 TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalhostURL) {
1031 history::VisitVector original_visits;
1032 // Create http and localhost url.
1033 history::URLRow url_entry(MakeTypedUrlEntry("http://yey.com",
1034 "yey", 12, 15, false,
1036 history::URLRow localhost_entry(MakeTypedUrlEntry("http://localhost",
1037 "localhost", 12, 15, false,
1040 history::URLRows original_entries;
1041 original_entries.push_back(url_entry);
1042 original_entries.push_back(localhost_entry);
1044 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)).
1045 WillRepeatedly(DoAll(SetArgumentPointee<0>(original_entries),
1047 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)).
1048 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits),
1050 CreateRootHelper create_root(this, syncer::TYPED_URLS);
1051 StartSyncService(create_root.callback());
1053 history::VisitVector updated_visits;
1054 // Update the previous entries and add a new localhost.
1055 history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com",
1056 "yey", 20, 15, false,
1058 history::URLRow updated_localhost_entry(MakeTypedUrlEntry(
1059 "http://localhost:80",
1060 "localhost", 20, 15, false,
1062 history::URLRow localhost_ip_entry(MakeTypedUrlEntry("http://127.0.0.1",
1063 "localhost", 12, 15, false,
1065 history::URLsModifiedDetails details;
1066 details.changed_urls.push_back(updated_url_entry);
1067 details.changed_urls.push_back(updated_localhost_entry);
1068 details.changed_urls.push_back(localhost_ip_entry);
1069 scoped_refptr<ThreadNotifier> notifier(
1070 new ThreadNotifier(history_thread_.get()));
1071 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
1072 content::Source<Profile>(profile_.get()),
1073 content::Details<history::URLsModifiedDetails>(&details));
1075 history::URLRows new_sync_entries;
1076 GetTypedUrlsFromSyncDB(&new_sync_entries);
1078 // We should ignore the localhost urls and left only with http url.
1079 ASSERT_EQ(1U, new_sync_entries.size());
1080 EXPECT_TRUE(URLsEqual(updated_url_entry, new_sync_entries[0]));