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. 4 5 #include <vector> 6 7 #include "testing/gtest/include/gtest/gtest.h" 8 9 #include "base/bind.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/profile_mock.h" 42 #include "chrome/test/base/testing_profile.h" 43 #include "components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h" 44 #include "content/public/browser/notification_service.h" 45 #include "google_apis/gaia/gaia_constants.h" 46 #include "sync/internal_api/public/read_node.h" 47 #include "sync/internal_api/public/read_transaction.h" 48 #include "sync/internal_api/public/write_node.h" 49 #include "sync/internal_api/public/write_transaction.h" 50 #include "sync/protocol/typed_url_specifics.pb.h" 51 #include "testing/gmock/include/gmock/gmock.h" 52 #include "url/gurl.h" 53 54 using base::Time; 55 using base::Thread; 56 using browser_sync::TypedUrlChangeProcessor; 57 using browser_sync::TypedUrlDataTypeController; 58 using browser_sync::TypedUrlModelAssociator; 59 using history::HistoryBackend; 60 using history::URLID; 61 using history::URLRow; 62 using syncer::syncable::WriteTransaction; 63 using testing::_; 64 using testing::DoAll; 65 using testing::Return; 66 using testing::SetArgumentPointee; 67 68 namespace { 69 // Visits with this timestamp are treated as expired. 70 static const int EXPIRED_VISIT = -1; 71 72 class HistoryBackendMock : public HistoryBackend { 73 public: 74 HistoryBackendMock() : HistoryBackend(base::FilePath(), 0, NULL, NULL) {} 75 virtual bool IsExpiredVisitTime(const base::Time& time) OVERRIDE { 76 return time.ToInternalValue() == EXPIRED_VISIT; 77 } 78 MOCK_METHOD1(GetAllTypedURLs, bool(history::URLRows* entries)); 79 MOCK_METHOD3(GetMostRecentVisitsForURL, bool(history::URLID id, 80 int max_visits, 81 history::VisitVector* visits)); 82 MOCK_METHOD2(UpdateURL, bool(history::URLID id, const history::URLRow& url)); 83 MOCK_METHOD3(AddVisits, bool(const GURL& url, 84 const std::vector<history::VisitInfo>& visits, 85 history::VisitSource visit_source)); 86 MOCK_METHOD1(RemoveVisits, bool(const history::VisitVector& visits)); 87 MOCK_METHOD2(GetURL, bool(const GURL& url_id, history::URLRow* url_row)); 88 MOCK_METHOD2(SetPageTitle, void(const GURL& url, const string16& title)); 89 MOCK_METHOD1(DeleteURL, void(const GURL& url)); 90 91 private: 92 virtual ~HistoryBackendMock() {} 93 }; 94 95 class HistoryServiceMock : public HistoryService { 96 public: 97 explicit HistoryServiceMock(Profile* profile) : HistoryService(profile) {} 98 MOCK_METHOD2(ScheduleDBTask, void(history::HistoryDBTask*, 99 CancelableRequestConsumerBase*)); 100 MOCK_METHOD0(Shutdown, void()); 101 102 void ShutdownBaseService() { 103 HistoryService::Shutdown(); 104 } 105 106 private: 107 virtual ~HistoryServiceMock() {} 108 }; 109 110 BrowserContextKeyedService* BuildHistoryService( 111 content::BrowserContext* profile) { 112 return new HistoryServiceMock(static_cast<Profile*>(profile)); 113 } 114 115 class TestTypedUrlModelAssociator : public TypedUrlModelAssociator { 116 public: 117 TestTypedUrlModelAssociator( 118 ProfileSyncService* sync_service, 119 history::HistoryBackend* history_backend, 120 browser_sync::DataTypeErrorHandler* error_handler) : 121 TypedUrlModelAssociator(sync_service, history_backend, error_handler) {} 122 123 protected: 124 // Don't clear error stats - that way we can verify their values in our 125 // tests. 126 virtual void ClearErrorStats() OVERRIDE {} 127 }; 128 129 void RunOnDBThreadCallback(HistoryBackend* backend, 130 history::HistoryDBTask* task) { 131 task->RunOnDBThread(backend, NULL); 132 } 133 134 ACTION_P2(RunTaskOnDBThread, thread, backend) { 135 // ScheduleDBTask takes ownership of its task argument, so we 136 // should, too. 137 scoped_refptr<history::HistoryDBTask> task(arg0); 138 thread->message_loop()->PostTask( 139 FROM_HERE, base::Bind(&RunOnDBThreadCallback, base::Unretained(backend), 140 task)); 141 } 142 143 ACTION_P2(ShutdownHistoryService, thread, service) { 144 service->ShutdownBaseService(); 145 delete thread; 146 } 147 148 ACTION_P6(MakeTypedUrlSyncComponents, 149 profile, 150 service, 151 hb, 152 dtc, 153 error_handler, 154 model_associator) { 155 *model_associator = 156 new TestTypedUrlModelAssociator(service, hb, error_handler); 157 TypedUrlChangeProcessor* change_processor = 158 new TypedUrlChangeProcessor(profile, *model_associator, hb, dtc); 159 return ProfileSyncComponentsFactory::SyncComponents(*model_associator, 160 change_processor); 161 } 162 163 class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest { 164 public: 165 void AddTypedUrlSyncNode(const history::URLRow& url, 166 const history::VisitVector& visits) { 167 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 168 syncer::ReadNode typed_url_root(&trans); 169 ASSERT_EQ(syncer::BaseNode::INIT_OK, 170 typed_url_root.InitByTagLookup(browser_sync::kTypedUrlTag)); 171 172 syncer::WriteNode node(&trans); 173 std::string tag = url.url().spec(); 174 syncer::WriteNode::InitUniqueByCreationResult result = 175 node.InitUniqueByCreation(syncer::TYPED_URLS, typed_url_root, tag); 176 ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result); 177 TypedUrlModelAssociator::WriteToSyncNode(url, visits, &node); 178 } 179 180 protected: 181 ProfileSyncServiceTypedUrlTest() { 182 history_thread_.reset(new Thread("history")); 183 } 184 185 virtual void SetUp() { 186 AbstractProfileSyncServiceTest::SetUp(); 187 profile_.reset(new ProfileMock()); 188 invalidation::InvalidationServiceFactory::GetInstance()-> 189 SetBuildOnlyFakeInvalidatorsForTest(true); 190 history_backend_ = new HistoryBackendMock(); 191 history_service_ = static_cast<HistoryServiceMock*>( 192 HistoryServiceFactory::GetInstance()->SetTestingFactoryAndUse( 193 profile_.get(), BuildHistoryService)); 194 EXPECT_CALL((*history_service_), ScheduleDBTask(_, _)) 195 .WillRepeatedly(RunTaskOnDBThread(history_thread_.get(), 196 history_backend_.get())); 197 history_thread_->Start(); 198 } 199 200 virtual void TearDown() { 201 EXPECT_CALL((*history_service_), Shutdown()) 202 .WillOnce(ShutdownHistoryService(history_thread_.release(), 203 history_service_)); 204 profile_.reset(); 205 AbstractProfileSyncServiceTest::TearDown(); 206 } 207 208 TypedUrlModelAssociator* StartSyncService(const base::Closure& callback) { 209 TypedUrlModelAssociator* model_associator = NULL; 210 if (!sync_service_) { 211 SigninManagerBase* signin = 212 SigninManagerFactory::GetForProfile(profile_.get()); 213 signin->SetAuthenticatedUsername("test"); 214 token_service_ = static_cast<TokenService*>( 215 TokenServiceFactory::GetInstance()->SetTestingFactoryAndUse( 216 profile_.get(), BuildTokenService)); 217 ProfileOAuth2TokenServiceFactory::GetInstance()->SetTestingFactory( 218 profile_.get(), FakeOAuth2TokenService::BuildTokenService); 219 sync_service_ = static_cast<TestProfileSyncService*>( 220 ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse( 221 profile_.get(), 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, 228 profile_.get(), 229 sync_service_); 230 231 EXPECT_CALL(*components, CreateTypedUrlSyncComponents(_, _, _)). 232 WillOnce(MakeTypedUrlSyncComponents(profile_.get(), 233 sync_service_, 234 history_backend_.get(), 235 data_type_controller, 236 &error_handler_, 237 &model_associator)); 238 EXPECT_CALL(*components, CreateDataTypeManager(_, _, _, _, _, _)). 239 WillOnce(ReturnNewDataTypeManager()); 240 241 token_service_->IssueAuthTokenForTest( 242 GaiaConstants::kGaiaOAuth2LoginRefreshToken, "oauth2_login_token"); 243 token_service_->IssueAuthTokenForTest( 244 GaiaConstants::kSyncService, "token"); 245 246 sync_service_->RegisterDataTypeController(data_type_controller); 247 248 sync_service_->Initialize(); 249 base::MessageLoop::current()->Run(); 250 } 251 return model_associator; 252 } 253 254 void GetTypedUrlsFromSyncDB(history::URLRows* urls) { 255 urls->clear(); 256 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 257 syncer::ReadNode typed_url_root(&trans); 258 if (typed_url_root.InitByTagLookup(browser_sync::kTypedUrlTag) != 259 syncer::BaseNode::INIT_OK) 260 return; 261 262 int64 child_id = typed_url_root.GetFirstChildId(); 263 while (child_id != syncer::kInvalidId) { 264 syncer::ReadNode child_node(&trans); 265 if (child_node.InitByIdLookup(child_id) != syncer::BaseNode::INIT_OK) 266 return; 267 268 const sync_pb::TypedUrlSpecifics& typed_url( 269 child_node.GetTypedUrlSpecifics()); 270 history::URLRow new_url(GURL(typed_url.url())); 271 272 new_url.set_title(UTF8ToUTF16(typed_url.title())); 273 DCHECK(typed_url.visits_size()); 274 DCHECK_EQ(typed_url.visits_size(), typed_url.visit_transitions_size()); 275 new_url.set_last_visit(base::Time::FromInternalValue( 276 typed_url.visits(typed_url.visits_size() - 1))); 277 new_url.set_hidden(typed_url.hidden()); 278 279 urls->push_back(new_url); 280 child_id = child_node.GetSuccessorId(); 281 } 282 } 283 284 void SetIdleChangeProcessorExpectations() { 285 EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).Times(0); 286 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).Times(0); 287 EXPECT_CALL((*history_backend_.get()), GetURL(_, _)).Times(0); 288 EXPECT_CALL((*history_backend_.get()), DeleteURL(_)).Times(0); 289 } 290 291 static bool URLsEqual(history::URLRow& lhs, history::URLRow& rhs) { 292 // Only verify the fields we explicitly sync (i.e. don't verify typed_count 293 // or visit_count because we rely on the history DB to manage those values 294 // and they are left unchanged by HistoryBackendMock). 295 return (lhs.url().spec().compare(rhs.url().spec()) == 0) && 296 (lhs.title().compare(rhs.title()) == 0) && 297 (lhs.last_visit() == rhs.last_visit()) && 298 (lhs.hidden() == rhs.hidden()); 299 } 300 301 static history::URLRow MakeTypedUrlEntry(const char* url, 302 const char* title, 303 int typed_count, 304 int64 last_visit, 305 bool hidden, 306 history::VisitVector* visits) { 307 // Give each URL a unique ID, to mimic the behavior of the real database. 308 static int unique_url_id = 0; 309 GURL gurl(url); 310 URLRow history_url(gurl, ++unique_url_id); 311 history_url.set_title(UTF8ToUTF16(title)); 312 history_url.set_typed_count(typed_count); 313 history_url.set_last_visit( 314 base::Time::FromInternalValue(last_visit)); 315 history_url.set_hidden(hidden); 316 visits->push_back(history::VisitRow( 317 history_url.id(), history_url.last_visit(), 0, 318 content::PAGE_TRANSITION_TYPED, 0)); 319 history_url.set_visit_count(visits->size()); 320 return history_url; 321 } 322 323 scoped_ptr<Thread> history_thread_; 324 325 scoped_ptr<ProfileMock> profile_; 326 scoped_refptr<HistoryBackendMock> history_backend_; 327 HistoryServiceMock* history_service_; 328 browser_sync::DataTypeErrorHandlerMock error_handler_; 329 }; 330 331 void AddTypedUrlEntries(ProfileSyncServiceTypedUrlTest* test, 332 const history::URLRows& entries) { 333 test->CreateRoot(syncer::TYPED_URLS); 334 for (size_t i = 0; i < entries.size(); ++i) { 335 history::VisitVector visits; 336 visits.push_back(history::VisitRow( 337 entries[i].id(), entries[i].last_visit(), 0, 338 content::PageTransitionFromInt(0), 0)); 339 test->AddTypedUrlSyncNode(entries[i], visits); 340 } 341 } 342 343 } // namespace 344 345 TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeEmptySync) { 346 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 347 WillOnce(Return(true)); 348 SetIdleChangeProcessorExpectations(); 349 CreateRootHelper create_root(this, syncer::TYPED_URLS); 350 TypedUrlModelAssociator* associator = 351 StartSyncService(create_root.callback()); 352 history::URLRows sync_entries; 353 GetTypedUrlsFromSyncDB(&sync_entries); 354 EXPECT_EQ(0U, sync_entries.size()); 355 ASSERT_EQ(0, associator->GetErrorPercentage()); 356 } 357 358 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeEmptySync) { 359 history::URLRows entries; 360 history::VisitVector visits; 361 entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar", 362 2, 15, false, &visits)); 363 364 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 365 WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); 366 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 367 WillRepeatedly(DoAll(SetArgumentPointee<2>(visits), Return(true))); 368 SetIdleChangeProcessorExpectations(); 369 CreateRootHelper create_root(this, syncer::TYPED_URLS); 370 TypedUrlModelAssociator* associator = 371 StartSyncService(create_root.callback()); 372 history::URLRows sync_entries; 373 GetTypedUrlsFromSyncDB(&sync_entries); 374 ASSERT_EQ(1U, sync_entries.size()); 375 EXPECT_TRUE(URLsEqual(entries[0], sync_entries[0])); 376 ASSERT_EQ(0, associator->GetErrorPercentage()); 377 } 378 379 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeErrorReadingVisits) { 380 history::URLRows entries; 381 history::VisitVector visits; 382 history::URLRow native_entry1(MakeTypedUrlEntry("http://foo.com", "bar", 383 2, 15, false, &visits)); 384 history::URLRow native_entry2(MakeTypedUrlEntry("http://foo2.com", "bar", 385 3, 15, false, &visits)); 386 entries.push_back(native_entry1); 387 entries.push_back(native_entry2); 388 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 389 WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); 390 // Return an error from GetMostRecentVisitsForURL() for the second URL. 391 EXPECT_CALL((*history_backend_.get()), 392 GetMostRecentVisitsForURL(native_entry1.id(), _, _)). 393 WillRepeatedly(Return(true)); 394 EXPECT_CALL((*history_backend_.get()), 395 GetMostRecentVisitsForURL(native_entry2.id(), _, _)). 396 WillRepeatedly(Return(false)); 397 SetIdleChangeProcessorExpectations(); 398 CreateRootHelper create_root(this, syncer::TYPED_URLS); 399 StartSyncService(create_root.callback()); 400 history::URLRows sync_entries; 401 GetTypedUrlsFromSyncDB(&sync_entries); 402 ASSERT_EQ(1U, sync_entries.size()); 403 EXPECT_TRUE(URLsEqual(native_entry1, sync_entries[0])); 404 } 405 406 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeWithBlankEmptySync) { 407 std::vector<history::URLRow> entries; 408 history::VisitVector visits; 409 // Add an empty URL. 410 entries.push_back(MakeTypedUrlEntry("", "bar", 411 2, 15, false, &visits)); 412 entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar", 413 2, 15, false, &visits)); 414 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 415 WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); 416 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 417 WillRepeatedly(DoAll(SetArgumentPointee<2>(visits), Return(true))); 418 SetIdleChangeProcessorExpectations(); 419 CreateRootHelper create_root(this, syncer::TYPED_URLS); 420 StartSyncService(create_root.callback()); 421 std::vector<history::URLRow> sync_entries; 422 GetTypedUrlsFromSyncDB(&sync_entries); 423 // The empty URL should be ignored. 424 ASSERT_EQ(1U, sync_entries.size()); 425 EXPECT_TRUE(URLsEqual(entries[1], sync_entries[0])); 426 } 427 428 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncNoMerge) { 429 history::VisitVector native_visits; 430 history::VisitVector sync_visits; 431 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", 432 2, 15, false, &native_visits)); 433 history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", 434 3, 16, false, &sync_visits)); 435 436 history::URLRows native_entries; 437 native_entries.push_back(native_entry); 438 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 439 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); 440 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 441 WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true))); 442 EXPECT_CALL((*history_backend_.get()), 443 AddVisits(_, _, history::SOURCE_SYNCED)).WillRepeatedly(Return(true)); 444 445 history::URLRows sync_entries; 446 sync_entries.push_back(sync_entry); 447 448 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)). 449 WillRepeatedly(Return(true)); 450 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); 451 452 std::map<std::string, history::URLRow> expected; 453 expected[native_entry.url().spec()] = native_entry; 454 expected[sync_entry.url().spec()] = sync_entry; 455 456 history::URLRows new_sync_entries; 457 GetTypedUrlsFromSyncDB(&new_sync_entries); 458 459 EXPECT_TRUE(new_sync_entries.size() == expected.size()); 460 for (history::URLRows::iterator entry = new_sync_entries.begin(); 461 entry != new_sync_entries.end(); ++entry) { 462 EXPECT_TRUE(URLsEqual(expected[entry->url().spec()], *entry)); 463 } 464 } 465 466 TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeExpiredSync) { 467 history::VisitVector sync_visits; 468 history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", 469 3, EXPIRED_VISIT, false, 470 &sync_visits)); 471 history::URLRows sync_entries; 472 sync_entries.push_back(sync_entry); 473 474 // Since all our URLs are expired, no backend calls to add new URLs will be 475 // made. 476 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 477 WillOnce(Return(true)); 478 SetIdleChangeProcessorExpectations(); 479 480 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); 481 } 482 483 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncMerge) { 484 history::VisitVector native_visits; 485 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", 486 2, 15, false, &native_visits)); 487 history::VisitVector sync_visits; 488 history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "name", 489 1, 17, false, &sync_visits)); 490 history::VisitVector merged_visits; 491 merged_visits.push_back(history::VisitRow( 492 sync_entry.id(), base::Time::FromInternalValue(15), 0, 493 content::PageTransitionFromInt(0), 0)); 494 495 history::URLRow merged_entry(MakeTypedUrlEntry("http://native.com", "name", 496 2, 17, false, &merged_visits)); 497 498 history::URLRows native_entries; 499 native_entries.push_back(native_entry); 500 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 501 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); 502 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 503 WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true))); 504 EXPECT_CALL((*history_backend_.get()), 505 AddVisits(_, _, history::SOURCE_SYNCED)). WillRepeatedly(Return(true)); 506 507 history::URLRows sync_entries; 508 sync_entries.push_back(sync_entry); 509 510 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)). 511 WillRepeatedly(Return(true)); 512 EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)). 513 WillRepeatedly(Return()); 514 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); 515 516 history::URLRows new_sync_entries; 517 GetTypedUrlsFromSyncDB(&new_sync_entries); 518 ASSERT_EQ(1U, new_sync_entries.size()); 519 EXPECT_TRUE(URLsEqual(merged_entry, new_sync_entries[0])); 520 } 521 522 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeWithErrorHasSyncMerge) { 523 history::VisitVector native_visits; 524 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "native", 525 2, 15, false, &native_visits)); 526 history::VisitVector sync_visits; 527 history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "sync", 528 1, 17, false, &sync_visits)); 529 530 history::URLRows native_entries; 531 native_entries.push_back(native_entry); 532 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 533 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); 534 // Return an error getting the visits for the native URL. 535 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 536 WillRepeatedly(Return(false)); 537 EXPECT_CALL((*history_backend_.get()), GetURL(_, _)). 538 WillRepeatedly(DoAll(SetArgumentPointee<1>(native_entry), Return(true))); 539 EXPECT_CALL((*history_backend_.get()), 540 AddVisits(_, _, history::SOURCE_SYNCED)). WillRepeatedly(Return(true)); 541 542 history::URLRows sync_entries; 543 sync_entries.push_back(sync_entry); 544 545 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)). 546 WillRepeatedly(Return(true)); 547 EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)). 548 WillRepeatedly(Return()); 549 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); 550 551 history::URLRows new_sync_entries; 552 GetTypedUrlsFromSyncDB(&new_sync_entries); 553 ASSERT_EQ(1U, new_sync_entries.size()); 554 EXPECT_TRUE(URLsEqual(sync_entry, new_sync_entries[0])); 555 } 556 557 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAdd) { 558 history::VisitVector added_visits; 559 history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", 560 2, 15, false, &added_visits)); 561 562 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 563 WillOnce(Return(true)); 564 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 565 WillOnce(DoAll(SetArgumentPointee<2>(added_visits), Return(true))); 566 567 SetIdleChangeProcessorExpectations(); 568 CreateRootHelper create_root(this, syncer::TYPED_URLS); 569 StartSyncService(create_root.callback()); 570 571 history::URLsModifiedDetails details; 572 details.changed_urls.push_back(added_entry); 573 scoped_refptr<ThreadNotifier> notifier( 574 new ThreadNotifier(history_thread_.get())); 575 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 576 content::Source<Profile>(profile_.get()), 577 content::Details<history::URLsModifiedDetails>(&details)); 578 579 history::URLRows new_sync_entries; 580 GetTypedUrlsFromSyncDB(&new_sync_entries); 581 ASSERT_EQ(1U, new_sync_entries.size()); 582 EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0])); 583 } 584 585 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAddWithBlank) { 586 history::VisitVector added_visits; 587 history::URLRow empty_entry(MakeTypedUrlEntry("", "entry", 588 2, 15, false, &added_visits)); 589 history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", 590 2, 15, false, &added_visits)); 591 592 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 593 WillOnce(Return(true)); 594 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 595 WillRepeatedly(DoAll(SetArgumentPointee<2>(added_visits), Return(true))); 596 597 SetIdleChangeProcessorExpectations(); 598 CreateRootHelper create_root(this, syncer::TYPED_URLS); 599 StartSyncService(create_root.callback()); 600 601 history::URLsModifiedDetails details; 602 details.changed_urls.push_back(empty_entry); 603 details.changed_urls.push_back(added_entry); 604 scoped_refptr<ThreadNotifier> notifier( 605 new ThreadNotifier(history_thread_.get())); 606 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 607 content::Source<Profile>(profile_.get()), 608 content::Details<history::URLsModifiedDetails>(&details)); 609 610 std::vector<history::URLRow> new_sync_entries; 611 GetTypedUrlsFromSyncDB(&new_sync_entries); 612 ASSERT_EQ(1U, new_sync_entries.size()); 613 EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0])); 614 } 615 616 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdate) { 617 history::VisitVector original_visits; 618 history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", 619 2, 15, false, 620 &original_visits)); 621 history::URLRows original_entries; 622 original_entries.push_back(original_entry); 623 624 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 625 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); 626 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 627 WillOnce(DoAll(SetArgumentPointee<2>(original_visits), 628 Return(true))); 629 CreateRootHelper create_root(this, syncer::TYPED_URLS); 630 StartSyncService(create_root.callback()); 631 632 history::VisitVector updated_visits; 633 history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", 634 7, 17, false, 635 &updated_visits)); 636 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 637 WillOnce(DoAll(SetArgumentPointee<2>(updated_visits), 638 Return(true))); 639 640 history::URLsModifiedDetails details; 641 details.changed_urls.push_back(updated_entry); 642 scoped_refptr<ThreadNotifier> notifier( 643 new ThreadNotifier(history_thread_.get())); 644 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 645 content::Source<Profile>(profile_.get()), 646 content::Details<history::URLsModifiedDetails>(&details)); 647 648 history::URLRows new_sync_entries; 649 GetTypedUrlsFromSyncDB(&new_sync_entries); 650 ASSERT_EQ(1U, new_sync_entries.size()); 651 EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0])); 652 } 653 654 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAddFromVisit) { 655 history::VisitVector added_visits; 656 history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", 657 2, 15, false, &added_visits)); 658 659 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 660 WillOnce(Return(true)); 661 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 662 WillOnce(DoAll(SetArgumentPointee<2>(added_visits), Return(true))); 663 664 SetIdleChangeProcessorExpectations(); 665 CreateRootHelper create_root(this, syncer::TYPED_URLS); 666 StartSyncService(create_root.callback()); 667 668 history::URLVisitedDetails details; 669 details.row = added_entry; 670 details.transition = content::PAGE_TRANSITION_TYPED; 671 scoped_refptr<ThreadNotifier> notifier( 672 new ThreadNotifier(history_thread_.get())); 673 notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED, 674 content::Source<Profile>(profile_.get()), 675 content::Details<history::URLVisitedDetails>(&details)); 676 677 history::URLRows new_sync_entries; 678 GetTypedUrlsFromSyncDB(&new_sync_entries); 679 ASSERT_EQ(1U, new_sync_entries.size()); 680 EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0])); 681 } 682 683 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdateFromVisit) { 684 history::VisitVector original_visits; 685 history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", 686 2, 15, false, 687 &original_visits)); 688 history::URLRows original_entries; 689 original_entries.push_back(original_entry); 690 691 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 692 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); 693 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 694 WillOnce(DoAll(SetArgumentPointee<2>(original_visits), 695 Return(true))); 696 CreateRootHelper create_root(this, syncer::TYPED_URLS); 697 StartSyncService(create_root.callback()); 698 699 history::VisitVector updated_visits; 700 history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", 701 7, 17, false, 702 &updated_visits)); 703 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 704 WillOnce(DoAll(SetArgumentPointee<2>(updated_visits), 705 Return(true))); 706 707 history::URLVisitedDetails details; 708 details.row = updated_entry; 709 details.transition = content::PAGE_TRANSITION_TYPED; 710 scoped_refptr<ThreadNotifier> notifier( 711 new ThreadNotifier(history_thread_.get())); 712 notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED, 713 content::Source<Profile>(profile_.get()), 714 content::Details<history::URLVisitedDetails>(&details)); 715 716 history::URLRows new_sync_entries; 717 GetTypedUrlsFromSyncDB(&new_sync_entries); 718 ASSERT_EQ(1U, new_sync_entries.size()); 719 EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0])); 720 } 721 722 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserIgnoreChangeUpdateFromVisit) { 723 history::VisitVector original_visits; 724 history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", 725 2, 15, false, 726 &original_visits)); 727 history::URLRows original_entries; 728 original_entries.push_back(original_entry); 729 730 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 731 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); 732 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 733 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits), 734 Return(true))); 735 CreateRootHelper create_root(this, syncer::TYPED_URLS); 736 StartSyncService(create_root.callback()); 737 history::URLRows new_sync_entries; 738 GetTypedUrlsFromSyncDB(&new_sync_entries); 739 ASSERT_EQ(1U, new_sync_entries.size()); 740 EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0])); 741 742 history::VisitVector updated_visits; 743 history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", 744 7, 15, false, 745 &updated_visits)); 746 history::URLVisitedDetails details; 747 details.row = updated_entry; 748 749 // Should ignore this change because it's not TYPED. 750 details.transition = content::PAGE_TRANSITION_RELOAD; 751 scoped_refptr<ThreadNotifier> notifier( 752 new ThreadNotifier(history_thread_.get())); 753 notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED, 754 content::Source<Profile>(profile_.get()), 755 content::Details<history::URLVisitedDetails>(&details)); 756 757 GetTypedUrlsFromSyncDB(&new_sync_entries); 758 759 // Should be no changes to the sync DB from this notification. 760 ASSERT_EQ(1U, new_sync_entries.size()); 761 EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0])); 762 763 // Now, try updating it with a large number of visits not divisible by 10 764 // (should ignore this visit). 765 history::URLRow twelve_visits(MakeTypedUrlEntry("http://mine.com", "entry", 766 12, 15, false, 767 &updated_visits)); 768 details.row = twelve_visits; 769 details.transition = content::PAGE_TRANSITION_TYPED; 770 notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED, 771 content::Source<Profile>(profile_.get()), 772 content::Details<history::URLVisitedDetails>(&details)); 773 GetTypedUrlsFromSyncDB(&new_sync_entries); 774 // Should be no changes to the sync DB from this notification. 775 ASSERT_EQ(1U, new_sync_entries.size()); 776 EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0])); 777 778 // Now, try updating it with a large number of visits that is divisible by 10 779 // (should *not* be ignored). 780 history::URLRow twenty_visits(MakeTypedUrlEntry("http://mine.com", "entry", 781 20, 15, false, 782 &updated_visits)); 783 details.row = twenty_visits; 784 details.transition = content::PAGE_TRANSITION_TYPED; 785 notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED, 786 content::Source<Profile>(profile_.get()), 787 content::Details<history::URLVisitedDetails>(&details)); 788 GetTypedUrlsFromSyncDB(&new_sync_entries); 789 ASSERT_EQ(1U, new_sync_entries.size()); 790 EXPECT_TRUE(URLsEqual(twenty_visits, new_sync_entries[0])); 791 } 792 793 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemove) { 794 history::VisitVector original_visits1; 795 history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", 796 2, 15, false, 797 &original_visits1)); 798 history::VisitVector original_visits2; 799 history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", 800 "entry2", 801 3, 15, false, 802 &original_visits2)); 803 history::URLRows original_entries; 804 original_entries.push_back(original_entry1); 805 original_entries.push_back(original_entry2); 806 807 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 808 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); 809 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 810 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1), 811 Return(true))); 812 CreateRootHelper create_root(this, syncer::TYPED_URLS); 813 StartSyncService(create_root.callback()); 814 815 history::URLsDeletedDetails changes; 816 changes.all_history = false; 817 changes.rows.push_back(history::URLRow(GURL("http://mine.com"))); 818 scoped_refptr<ThreadNotifier> notifier( 819 new ThreadNotifier(history_thread_.get())); 820 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED, 821 content::Source<Profile>(profile_.get()), 822 content::Details<history::URLsDeletedDetails>(&changes)); 823 824 history::URLRows new_sync_entries; 825 GetTypedUrlsFromSyncDB(&new_sync_entries); 826 ASSERT_EQ(1U, new_sync_entries.size()); 827 EXPECT_TRUE(URLsEqual(original_entry2, new_sync_entries[0])); 828 } 829 830 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveArchive) { 831 history::VisitVector original_visits1; 832 history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", 833 2, 15, false, 834 &original_visits1)); 835 history::VisitVector original_visits2; 836 history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", 837 "entry2", 838 3, 15, false, 839 &original_visits2)); 840 history::URLRows original_entries; 841 original_entries.push_back(original_entry1); 842 original_entries.push_back(original_entry2); 843 844 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 845 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); 846 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 847 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1), 848 Return(true))); 849 CreateRootHelper create_root(this, syncer::TYPED_URLS); 850 StartSyncService(create_root.callback()); 851 852 history::URLsDeletedDetails changes; 853 changes.all_history = false; 854 // Setting archived=true should cause the sync code to ignore this deletion. 855 changes.archived = true; 856 changes.rows.push_back(history::URLRow(GURL("http://mine.com"))); 857 scoped_refptr<ThreadNotifier> notifier( 858 new ThreadNotifier(history_thread_.get())); 859 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED, 860 content::Source<Profile>(profile_.get()), 861 content::Details<history::URLsDeletedDetails>(&changes)); 862 863 history::URLRows new_sync_entries; 864 GetTypedUrlsFromSyncDB(&new_sync_entries); 865 // Both URLs should still be there. 866 ASSERT_EQ(2U, new_sync_entries.size()); 867 } 868 869 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveAll) { 870 history::VisitVector original_visits1; 871 history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", 872 2, 15, false, 873 &original_visits1)); 874 history::VisitVector original_visits2; 875 history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", 876 "entry2", 877 3, 15, false, 878 &original_visits2)); 879 history::URLRows original_entries; 880 original_entries.push_back(original_entry1); 881 original_entries.push_back(original_entry2); 882 883 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 884 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); 885 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 886 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1), 887 Return(true))); 888 CreateRootHelper create_root(this, syncer::TYPED_URLS); 889 StartSyncService(create_root.callback()); 890 891 history::URLRows new_sync_entries; 892 GetTypedUrlsFromSyncDB(&new_sync_entries); 893 ASSERT_EQ(2U, new_sync_entries.size()); 894 895 history::URLsDeletedDetails changes; 896 changes.all_history = true; 897 scoped_refptr<ThreadNotifier> notifier( 898 new ThreadNotifier(history_thread_.get())); 899 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED, 900 content::Source<Profile>(profile_.get()), 901 content::Details<history::URLsDeletedDetails>(&changes)); 902 903 GetTypedUrlsFromSyncDB(&new_sync_entries); 904 ASSERT_EQ(0U, new_sync_entries.size()); 905 } 906 907 TEST_F(ProfileSyncServiceTypedUrlTest, FailWriteToHistoryBackend) { 908 history::VisitVector native_visits; 909 history::VisitVector sync_visits; 910 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", 911 2, 15, false, &native_visits)); 912 history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", 913 3, 16, false, &sync_visits)); 914 915 history::URLRows native_entries; 916 native_entries.push_back(native_entry); 917 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 918 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); 919 EXPECT_CALL((*history_backend_.get()), GetURL(_, _)). 920 WillOnce(DoAll(SetArgumentPointee<1>(native_entry), Return(true))); 921 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 922 WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true))); 923 EXPECT_CALL((*history_backend_.get()), 924 AddVisits(_, _, history::SOURCE_SYNCED)).WillRepeatedly(Return(false)); 925 926 history::URLRows sync_entries; 927 sync_entries.push_back(sync_entry); 928 929 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)). 930 WillRepeatedly(Return(false)); 931 TypedUrlModelAssociator* associator = 932 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); 933 // Errors writing to the DB should be recorded, but should not cause an 934 // unrecoverable error. 935 ASSERT_FALSE( 936 sync_service_->failed_data_types_handler().GetFailedTypes().Has( 937 syncer::TYPED_URLS)); 938 // Some calls should have succeeded, so the error percentage should be 939 // somewhere > 0 and < 100. 940 ASSERT_NE(0, associator->GetErrorPercentage()); 941 ASSERT_NE(100, associator->GetErrorPercentage()); 942 } 943 944 TEST_F(ProfileSyncServiceTypedUrlTest, FailToGetTypedURLs) { 945 history::VisitVector native_visits; 946 history::VisitVector sync_visits; 947 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", 948 2, 15, false, &native_visits)); 949 history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", 950 3, 16, false, &sync_visits)); 951 952 history::URLRows native_entries; 953 native_entries.push_back(native_entry); 954 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 955 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(false))); 956 957 history::URLRows sync_entries; 958 sync_entries.push_back(sync_entry); 959 960 EXPECT_CALL(error_handler_, CreateAndUploadError(_, _, _)). 961 WillOnce(Return(syncer::SyncError( 962 FROM_HERE, 963 syncer::SyncError::DATATYPE_ERROR, 964 "Unit test", 965 syncer::TYPED_URLS))); 966 StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); 967 // Errors getting typed URLs will cause an unrecoverable error (since we can 968 // do *nothing* in that case). 969 ASSERT_TRUE( 970 sync_service_->failed_data_types_handler().GetFailedTypes().Has( 971 syncer::TYPED_URLS)); 972 ASSERT_EQ( 973 1u, sync_service_->failed_data_types_handler().GetFailedTypes().Size()); 974 // Can't check GetErrorPercentage(), because generating an unrecoverable 975 // error will free the model associator. 976 } 977 978 TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalFileURL) { 979 history::VisitVector original_visits; 980 // Create http and file url. 981 history::URLRow url_entry(MakeTypedUrlEntry("http://yey.com", 982 "yey", 12, 15, false, 983 &original_visits)); 984 history::URLRow file_entry(MakeTypedUrlEntry("file:///kitty.jpg", 985 "kitteh", 12, 15, false, 986 &original_visits)); 987 988 history::URLRows original_entries; 989 original_entries.push_back(url_entry); 990 original_entries.push_back(file_entry); 991 992 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 993 WillRepeatedly(DoAll(SetArgumentPointee<0>(original_entries), 994 Return(true))); 995 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 996 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits), 997 Return(true))); 998 CreateRootHelper create_root(this, syncer::TYPED_URLS); 999 StartSyncService(create_root.callback()); 1000 1001 history::VisitVector updated_visits; 1002 // Create updates for the previous urls + a new file one. 1003 history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com", 1004 "yey", 20, 15, false, 1005 &updated_visits)); 1006 history::URLRow updated_file_entry(MakeTypedUrlEntry("file:///cat.jpg", 1007 "cat", 20, 15, false, 1008 &updated_visits)); 1009 history::URLRow new_file_entry(MakeTypedUrlEntry("file:///dog.jpg", 1010 "dog", 20, 15, false, 1011 &updated_visits)); 1012 history::URLsModifiedDetails details; 1013 details.changed_urls.push_back(updated_url_entry); 1014 details.changed_urls.push_back(updated_file_entry); 1015 details.changed_urls.push_back(new_file_entry); 1016 scoped_refptr<ThreadNotifier> notifier( 1017 new ThreadNotifier(history_thread_.get())); 1018 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 1019 content::Source<Profile>(profile_.get()), 1020 content::Details<history::URLsModifiedDetails>(&details)); 1021 1022 history::URLRows new_sync_entries; 1023 GetTypedUrlsFromSyncDB(&new_sync_entries); 1024 1025 // We should ignore the local file urls (existing and updated), 1026 // and only be left with the updated http url. 1027 ASSERT_EQ(1U, new_sync_entries.size()); 1028 EXPECT_TRUE(URLsEqual(updated_url_entry, new_sync_entries[0])); 1029 } 1030 1031 TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalhostURL) { 1032 history::VisitVector original_visits; 1033 // Create http and localhost url. 1034 history::URLRow url_entry(MakeTypedUrlEntry("http://yey.com", 1035 "yey", 12, 15, false, 1036 &original_visits)); 1037 history::URLRow localhost_entry(MakeTypedUrlEntry("http://localhost", 1038 "localhost", 12, 15, false, 1039 &original_visits)); 1040 1041 history::URLRows original_entries; 1042 original_entries.push_back(url_entry); 1043 original_entries.push_back(localhost_entry); 1044 1045 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 1046 WillRepeatedly(DoAll(SetArgumentPointee<0>(original_entries), 1047 Return(true))); 1048 EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). 1049 WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits), 1050 Return(true))); 1051 CreateRootHelper create_root(this, syncer::TYPED_URLS); 1052 StartSyncService(create_root.callback()); 1053 1054 history::VisitVector updated_visits; 1055 // Update the previous entries and add a new localhost. 1056 history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com", 1057 "yey", 20, 15, false, 1058 &updated_visits)); 1059 history::URLRow updated_localhost_entry(MakeTypedUrlEntry( 1060 "http://localhost:80", 1061 "localhost", 20, 15, false, 1062 &original_visits)); 1063 history::URLRow localhost_ip_entry(MakeTypedUrlEntry("http://127.0.0.1", 1064 "localhost", 12, 15, false, 1065 &original_visits)); 1066 history::URLsModifiedDetails details; 1067 details.changed_urls.push_back(updated_url_entry); 1068 details.changed_urls.push_back(updated_localhost_entry); 1069 details.changed_urls.push_back(localhost_ip_entry); 1070 scoped_refptr<ThreadNotifier> notifier( 1071 new ThreadNotifier(history_thread_.get())); 1072 notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 1073 content::Source<Profile>(profile_.get()), 1074 content::Details<history::URLsModifiedDetails>(&details)); 1075 1076 history::URLRows new_sync_entries; 1077 GetTypedUrlsFromSyncDB(&new_sync_entries); 1078 1079 // We should ignore the localhost urls and left only with http url. 1080 ASSERT_EQ(1U, new_sync_entries.size()); 1081 EXPECT_TRUE(URLsEqual(updated_url_entry, new_sync_entries[0])); 1082 } 1083