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