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