1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <vector> 6 7 #include "testing/gtest/include/gtest/gtest.h" 8 9 #include "base/memory/ref_counted.h" 10 #include "base/string16.h" 11 #include "base/threading/thread.h" 12 #include "base/time.h" 13 #include "chrome/browser/history/history_backend.h" 14 #include "chrome/browser/history/history_notifications.h" 15 #include "chrome/browser/history/history_types.h" 16 #include "chrome/browser/sync/abstract_profile_sync_service_test.h" 17 #include "chrome/browser/sync/engine/syncapi.h" 18 #include "chrome/browser/sync/glue/sync_backend_host.h" 19 #include "chrome/browser/sync/glue/sync_backend_host_mock.h" 20 #include "chrome/browser/sync/glue/typed_url_change_processor.h" 21 #include "chrome/browser/sync/glue/typed_url_data_type_controller.h" 22 #include "chrome/browser/sync/glue/typed_url_model_associator.h" 23 #include "chrome/browser/sync/profile_sync_factory.h" 24 #include "chrome/browser/sync/profile_sync_factory_mock.h" 25 #include "chrome/browser/sync/profile_sync_service.h" 26 #include "chrome/browser/sync/profile_sync_test_util.h" 27 #include "chrome/browser/sync/protocol/typed_url_specifics.pb.h" 28 #include "chrome/browser/sync/syncable/directory_manager.h" 29 #include "chrome/browser/sync/test_profile_sync_service.h" 30 #include "chrome/common/net/gaia/gaia_constants.h" 31 #include "chrome/test/profile_mock.h" 32 #include "chrome/test/sync/engine/test_id_factory.h" 33 #include "chrome/test/testing_profile.h" 34 #include "content/common/notification_service.h" 35 #include "googleurl/src/gurl.h" 36 #include "testing/gmock/include/gmock/gmock.h" 37 38 using base::Time; 39 using base::Thread; 40 using browser_sync::SyncBackendHost; 41 using browser_sync::SyncBackendHostMock; 42 using browser_sync::TestIdFactory; 43 using browser_sync::TypedUrlChangeProcessor; 44 using browser_sync::TypedUrlDataTypeController; 45 using browser_sync::TypedUrlModelAssociator; 46 using browser_sync::UnrecoverableErrorHandler; 47 using history::HistoryBackend; 48 using history::URLID; 49 using history::URLRow; 50 using sync_api::SyncManager; 51 using sync_api::UserShare; 52 using syncable::BASE_VERSION; 53 using syncable::CREATE; 54 using syncable::DirectoryManager; 55 using syncable::IS_DEL; 56 using syncable::IS_DIR; 57 using syncable::IS_UNAPPLIED_UPDATE; 58 using syncable::IS_UNSYNCED; 59 using syncable::MutableEntry; 60 using syncable::SERVER_IS_DIR; 61 using syncable::SERVER_VERSION; 62 using syncable::SPECIFICS; 63 using syncable::ScopedDirLookup; 64 using syncable::UNIQUE_SERVER_TAG; 65 using syncable::UNITTEST; 66 using syncable::WriteTransaction; 67 using testing::_; 68 using testing::DoAll; 69 using testing::DoDefault; 70 using testing::Invoke; 71 using testing::Return; 72 using testing::SetArgumentPointee; 73 using testing::WithArgs; 74 75 class HistoryBackendMock : public HistoryBackend { 76 public: 77 HistoryBackendMock() : HistoryBackend(FilePath(), NULL, NULL) {} 78 MOCK_METHOD1(GetAllTypedURLs, bool(std::vector<history::URLRow>* entries)); 79 MOCK_METHOD2(GetVisitsForURL, bool(history::URLID id, 80 history::VisitVector* visits)); 81 MOCK_METHOD2(UpdateURL, bool(history::URLID id, const history::URLRow& url)); 82 MOCK_METHOD3(AddVisits, bool(const GURL& url, 83 const std::vector<base::Time>& visits, 84 history::VisitSource visit_source)); 85 MOCK_METHOD1(RemoveVisits, bool(const history::VisitVector& visits)); 86 MOCK_METHOD2(GetURL, bool(const GURL& url_id, history::URLRow* url_row)); 87 MOCK_METHOD2(SetPageTitle, void(const GURL& url, const string16& title)); 88 MOCK_METHOD1(DeleteURL, void(const GURL& url)); 89 }; 90 91 class HistoryServiceMock : public HistoryService { 92 public: 93 HistoryServiceMock() {} 94 MOCK_METHOD2(ScheduleDBTask, Handle(HistoryDBTask*, 95 CancelableRequestConsumerBase*)); 96 }; 97 98 class RunOnDBThreadTask : public Task { 99 public: 100 RunOnDBThreadTask(HistoryBackend* backend, HistoryDBTask* task) 101 : backend_(backend), task_(task) {} 102 virtual void Run() { 103 task_->RunOnDBThread(backend_, NULL); 104 task_ = NULL; 105 } 106 private: 107 HistoryBackend* backend_; 108 scoped_refptr<HistoryDBTask> task_; 109 }; 110 111 ACTION_P2(RunTaskOnDBThread, thread, backend) { 112 thread->message_loop()->PostTask( 113 FROM_HERE, 114 new RunOnDBThreadTask(backend, arg0)); 115 return 0; 116 } 117 118 ACTION_P3(MakeTypedUrlSyncComponents, service, hb, dtc) { 119 TypedUrlModelAssociator* model_associator = 120 new TypedUrlModelAssociator(service, hb); 121 TypedUrlChangeProcessor* change_processor = 122 new TypedUrlChangeProcessor(model_associator, hb, service); 123 return ProfileSyncFactory::SyncComponents(model_associator, change_processor); 124 } 125 126 class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest { 127 protected: 128 ProfileSyncServiceTypedUrlTest() 129 : history_thread_("history") { 130 } 131 132 virtual void SetUp() { 133 profile_.CreateRequestContext(); 134 history_backend_ = new HistoryBackendMock(); 135 history_service_ = new HistoryServiceMock(); 136 EXPECT_CALL((*history_service_.get()), ScheduleDBTask(_, _)) 137 .WillRepeatedly(RunTaskOnDBThread(&history_thread_, 138 history_backend_.get())); 139 history_thread_.Start(); 140 141 notification_service_ = 142 new ThreadNotificationService(&history_thread_); 143 notification_service_->Init(); 144 } 145 146 virtual void TearDown() { 147 history_backend_ = NULL; 148 history_service_ = NULL; 149 service_.reset(); 150 notification_service_->TearDown(); 151 history_thread_.Stop(); 152 { 153 // The request context gets deleted on the I/O thread. To prevent a leak 154 // supply one here. 155 BrowserThread io_thread(BrowserThread::IO, MessageLoop::current()); 156 profile_.ResetRequestContext(); 157 } 158 MessageLoop::current()->RunAllPending(); 159 } 160 161 void StartSyncService(Task* task) { 162 if (!service_.get()) { 163 service_.reset( 164 new TestProfileSyncService(&factory_, &profile_, "test", false, 165 task)); 166 TypedUrlDataTypeController* data_type_controller = 167 new TypedUrlDataTypeController(&factory_, 168 &profile_, 169 service_.get()); 170 171 EXPECT_CALL(factory_, CreateTypedUrlSyncComponents(_, _, _)). 172 WillOnce(MakeTypedUrlSyncComponents(service_.get(), 173 history_backend_.get(), 174 data_type_controller)); 175 EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). 176 WillOnce(ReturnNewDataTypeManager()); 177 178 EXPECT_CALL(profile_, GetHistoryServiceWithoutCreating()). 179 WillRepeatedly(Return(history_service_.get())); 180 181 EXPECT_CALL(profile_, GetPasswordStore(_)). 182 WillOnce(Return(static_cast<PasswordStore*>(NULL))); 183 184 EXPECT_CALL(profile_, GetHistoryService(_)). 185 WillRepeatedly(Return(history_service_.get())); 186 187 token_service_.IssueAuthTokenForTest( 188 GaiaConstants::kSyncService, "token"); 189 190 EXPECT_CALL(profile_, GetTokenService()). 191 WillRepeatedly(Return(&token_service_)); 192 193 service_->RegisterDataTypeController(data_type_controller); 194 195 service_->Initialize(); 196 MessageLoop::current()->Run(); 197 } 198 } 199 200 void AddTypedUrlSyncNode(const history::URLRow& url, 201 const history::VisitVector& visits) { 202 sync_api::WriteTransaction trans(service_->GetUserShare()); 203 sync_api::ReadNode typed_url_root(&trans); 204 ASSERT_TRUE(typed_url_root.InitByTagLookup(browser_sync::kTypedUrlTag)); 205 206 sync_api::WriteNode node(&trans); 207 std::string tag = url.url().spec(); 208 ASSERT_TRUE(node.InitUniqueByCreation(syncable::TYPED_URLS, 209 typed_url_root, 210 tag)); 211 TypedUrlModelAssociator::WriteToSyncNode(url, visits, &node); 212 } 213 214 void GetTypedUrlsFromSyncDB(std::vector<history::URLRow>* urls) { 215 sync_api::ReadTransaction trans(service_->GetUserShare()); 216 sync_api::ReadNode typed_url_root(&trans); 217 if (!typed_url_root.InitByTagLookup(browser_sync::kTypedUrlTag)) 218 return; 219 220 int64 child_id = typed_url_root.GetFirstChildId(); 221 while (child_id != sync_api::kInvalidId) { 222 sync_api::ReadNode child_node(&trans); 223 if (!child_node.InitByIdLookup(child_id)) 224 return; 225 226 const sync_pb::TypedUrlSpecifics& typed_url( 227 child_node.GetTypedUrlSpecifics()); 228 history::URLRow new_url(GURL(typed_url.url())); 229 230 new_url.set_title(UTF8ToUTF16(typed_url.title())); 231 new_url.set_typed_count(typed_url.typed_count()); 232 DCHECK(typed_url.visit_size()); 233 new_url.set_visit_count(typed_url.visit_size()); 234 new_url.set_last_visit(base::Time::FromInternalValue( 235 typed_url.visit(typed_url.visit_size() - 1))); 236 new_url.set_hidden(typed_url.hidden()); 237 238 urls->push_back(new_url); 239 child_id = child_node.GetSuccessorId(); 240 } 241 } 242 243 void SetIdleChangeProcessorExpectations() { 244 EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).Times(0); 245 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).Times(0); 246 EXPECT_CALL((*history_backend_.get()), GetURL(_, _)).Times(0); 247 EXPECT_CALL((*history_backend_.get()), DeleteURL(_)).Times(0); 248 } 249 250 static bool URLsEqual(history::URLRow& lhs, history::URLRow& rhs) { 251 return (lhs.url().spec().compare(rhs.url().spec()) == 0) && 252 (lhs.title().compare(rhs.title()) == 0) && 253 (lhs.visit_count() == rhs.visit_count()) && 254 (lhs.typed_count() == rhs.typed_count()) && 255 (lhs.last_visit() == rhs.last_visit()) && 256 (lhs.hidden() == rhs.hidden()); 257 } 258 259 static history::URLRow MakeTypedUrlEntry(const char* url, 260 const char* title, 261 int typed_count, 262 int64 last_visit, 263 bool hidden, 264 history::VisitVector* visits) { 265 GURL gurl(url); 266 URLRow history_url(gurl); 267 history_url.set_title(UTF8ToUTF16(title)); 268 history_url.set_typed_count(typed_count); 269 history_url.set_last_visit( 270 base::Time::FromInternalValue(last_visit)); 271 history_url.set_hidden(hidden); 272 visits->push_back(history::VisitRow( 273 history_url.id(), history_url.last_visit(), 0, 0, 0)); 274 history_url.set_visit_count(visits->size()); 275 return history_url; 276 } 277 278 friend class AddTypedUrlEntriesTask; 279 friend class CreateTypedUrlRootTask; 280 281 Thread history_thread_; 282 scoped_refptr<ThreadNotificationService> notification_service_; 283 284 ProfileMock profile_; 285 ProfileSyncFactoryMock factory_; 286 scoped_refptr<HistoryBackendMock> history_backend_; 287 scoped_refptr<HistoryServiceMock> history_service_; 288 }; 289 290 class AddTypedUrlEntriesTask : public Task { 291 public: 292 AddTypedUrlEntriesTask(ProfileSyncServiceTypedUrlTest* test, 293 const std::vector<history::URLRow>& entries) 294 : test_(test), entries_(entries) { 295 } 296 297 virtual void Run() { 298 test_->CreateRoot(syncable::TYPED_URLS); 299 for (size_t i = 0; i < entries_.size(); ++i) { 300 history::VisitVector visits; 301 visits.push_back(history::VisitRow( 302 entries_[i].id(), entries_[i].last_visit(), 0, 0, 0)); 303 test_->AddTypedUrlSyncNode(entries_[i], visits); 304 } 305 } 306 307 private: 308 ProfileSyncServiceTypedUrlTest* test_; 309 const std::vector<history::URLRow>& entries_; 310 }; 311 312 TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeEmptySync) { 313 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 314 WillOnce(Return(true)); 315 SetIdleChangeProcessorExpectations(); 316 CreateRootTask task(this, syncable::TYPED_URLS); 317 StartSyncService(&task); 318 std::vector<history::URLRow> sync_entries; 319 GetTypedUrlsFromSyncDB(&sync_entries); 320 EXPECT_EQ(0U, sync_entries.size()); 321 } 322 323 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeEmptySync) { 324 std::vector<history::URLRow> entries; 325 history::VisitVector visits; 326 entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar", 327 2, 15, false, &visits)); 328 329 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 330 WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); 331 EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). 332 WillRepeatedly(DoAll(SetArgumentPointee<1>(visits), Return(true))); 333 SetIdleChangeProcessorExpectations(); 334 CreateRootTask task(this, syncable::TYPED_URLS); 335 StartSyncService(&task); 336 std::vector<history::URLRow> sync_entries; 337 GetTypedUrlsFromSyncDB(&sync_entries); 338 ASSERT_EQ(1U, sync_entries.size()); 339 EXPECT_TRUE(URLsEqual(entries[0], sync_entries[0])); 340 } 341 342 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncNoMerge) { 343 history::VisitVector native_visits; 344 history::VisitVector sync_visits; 345 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", 346 2, 15, false, &native_visits)); 347 history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", 348 3, 16, false, &sync_visits)); 349 350 std::vector<history::URLRow> native_entries; 351 native_entries.push_back(native_entry); 352 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 353 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); 354 EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). 355 WillRepeatedly(DoAll(SetArgumentPointee<1>(native_visits), Return(true))); 356 EXPECT_CALL((*history_backend_.get()), 357 AddVisits(_, _, history::SOURCE_SYNCED)).WillRepeatedly(Return(true)); 358 359 std::vector<history::URLRow> sync_entries; 360 sync_entries.push_back(sync_entry); 361 AddTypedUrlEntriesTask task(this, sync_entries); 362 363 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)). 364 WillRepeatedly(Return(true)); 365 StartSyncService(&task); 366 367 std::map<std::string, history::URLRow> expected; 368 expected[native_entry.url().spec()] = native_entry; 369 expected[sync_entry.url().spec()] = sync_entry; 370 371 std::vector<history::URLRow> new_sync_entries; 372 GetTypedUrlsFromSyncDB(&new_sync_entries); 373 374 EXPECT_TRUE(new_sync_entries.size() == expected.size()); 375 for (std::vector<history::URLRow>::iterator entry = new_sync_entries.begin(); 376 entry != new_sync_entries.end(); ++entry) { 377 EXPECT_TRUE(URLsEqual(expected[entry->url().spec()], *entry)); 378 } 379 } 380 381 TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncMerge) { 382 history::VisitVector native_visits; 383 history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", 384 2, 15, false, &native_visits)); 385 history::VisitVector sync_visits; 386 history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "name", 387 1, 17, false, &sync_visits)); 388 history::VisitVector merged_visits; 389 merged_visits.push_back(history::VisitRow( 390 sync_entry.id(), base::Time::FromInternalValue(15), 0, 0, 0)); 391 392 history::URLRow merged_entry(MakeTypedUrlEntry("http://native.com", "name", 393 2, 17, false, &merged_visits)); 394 395 std::vector<history::URLRow> native_entries; 396 native_entries.push_back(native_entry); 397 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 398 WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); 399 EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). 400 WillRepeatedly(DoAll(SetArgumentPointee<1>(native_visits), Return(true))); 401 EXPECT_CALL((*history_backend_.get()), 402 AddVisits(_, _, history::SOURCE_SYNCED)). WillRepeatedly(Return(true)); 403 404 std::vector<history::URLRow> sync_entries; 405 sync_entries.push_back(sync_entry); 406 AddTypedUrlEntriesTask task(this, sync_entries); 407 408 EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)). 409 WillRepeatedly(Return(true)); 410 EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)). 411 WillRepeatedly(Return()); 412 StartSyncService(&task); 413 414 std::vector<history::URLRow> new_sync_entries; 415 GetTypedUrlsFromSyncDB(&new_sync_entries); 416 ASSERT_EQ(1U, new_sync_entries.size()); 417 EXPECT_TRUE(URLsEqual(merged_entry, new_sync_entries[0])); 418 } 419 420 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAdd) { 421 history::VisitVector added_visits; 422 history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", 423 2, 15, false, &added_visits)); 424 425 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 426 WillOnce(Return(true)); 427 EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). 428 WillOnce(DoAll(SetArgumentPointee<1>(added_visits), Return(true))); 429 430 SetIdleChangeProcessorExpectations(); 431 CreateRootTask task(this, syncable::TYPED_URLS); 432 StartSyncService(&task); 433 434 history::URLsModifiedDetails details; 435 details.changed_urls.push_back(added_entry); 436 scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_)); 437 notifier->Notify(NotificationType::HISTORY_TYPED_URLS_MODIFIED, 438 Details<history::URLsModifiedDetails>(&details)); 439 440 std::vector<history::URLRow> new_sync_entries; 441 GetTypedUrlsFromSyncDB(&new_sync_entries); 442 ASSERT_EQ(1U, new_sync_entries.size()); 443 EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0])); 444 } 445 446 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdate) { 447 history::VisitVector original_visits; 448 history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", 449 2, 15, false, 450 &original_visits)); 451 std::vector<history::URLRow> original_entries; 452 original_entries.push_back(original_entry); 453 454 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 455 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); 456 EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). 457 WillRepeatedly(DoAll(SetArgumentPointee<1>(original_visits), 458 Return(true))); 459 CreateRootTask task(this, syncable::TYPED_URLS); 460 StartSyncService(&task); 461 462 history::VisitVector updated_visits; 463 history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", 464 7, 15, false, 465 &updated_visits)); 466 467 history::URLsModifiedDetails details; 468 details.changed_urls.push_back(updated_entry); 469 scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_)); 470 notifier->Notify(NotificationType::HISTORY_TYPED_URLS_MODIFIED, 471 Details<history::URLsModifiedDetails>(&details)); 472 473 std::vector<history::URLRow> new_sync_entries; 474 GetTypedUrlsFromSyncDB(&new_sync_entries); 475 ASSERT_EQ(1U, new_sync_entries.size()); 476 EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0])); 477 } 478 479 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemove) { 480 history::VisitVector original_visits1; 481 history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", 482 2, 15, false, 483 &original_visits1)); 484 history::VisitVector original_visits2; 485 history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", 486 "entry2", 487 3, 15, false, 488 &original_visits2)); 489 std::vector<history::URLRow> original_entries; 490 original_entries.push_back(original_entry1); 491 original_entries.push_back(original_entry2); 492 493 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 494 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); 495 EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). 496 WillRepeatedly(DoAll(SetArgumentPointee<1>(original_visits1), 497 Return(true))); 498 CreateRootTask task(this, syncable::TYPED_URLS); 499 StartSyncService(&task); 500 501 history::URLsDeletedDetails changes; 502 changes.all_history = false; 503 changes.urls.insert(GURL("http://mine.com")); 504 scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_)); 505 notifier->Notify(NotificationType::HISTORY_URLS_DELETED, 506 Details<history::URLsDeletedDetails>(&changes)); 507 508 std::vector<history::URLRow> new_sync_entries; 509 GetTypedUrlsFromSyncDB(&new_sync_entries); 510 ASSERT_EQ(1U, new_sync_entries.size()); 511 EXPECT_TRUE(URLsEqual(original_entry2, new_sync_entries[0])); 512 } 513 514 TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveAll) { 515 history::VisitVector original_visits1; 516 history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", 517 2, 15, false, 518 &original_visits1)); 519 history::VisitVector original_visits2; 520 history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", 521 "entry2", 522 3, 15, false, 523 &original_visits2)); 524 std::vector<history::URLRow> original_entries; 525 original_entries.push_back(original_entry1); 526 original_entries.push_back(original_entry2); 527 528 EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). 529 WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); 530 EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). 531 WillRepeatedly(DoAll(SetArgumentPointee<1>(original_visits1), 532 Return(true))); 533 CreateRootTask task(this, syncable::TYPED_URLS); 534 StartSyncService(&task); 535 536 history::URLsDeletedDetails changes; 537 changes.all_history = true; 538 scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_)); 539 notifier->Notify(NotificationType::HISTORY_URLS_DELETED, 540 Details<history::URLsDeletedDetails>(&changes)); 541 542 std::vector<history::URLRow> new_sync_entries; 543 GetTypedUrlsFromSyncDB(&new_sync_entries); 544 ASSERT_EQ(0U, new_sync_entries.size()); 545 } 546