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 <map> 6 #include <string> 7 8 #include "base/bind.h" 9 #include "base/bind_helpers.h" 10 #include "base/callback.h" 11 #include "base/compiler_specific.h" 12 #include "base/files/scoped_temp_dir.h" 13 #include "base/guid.h" 14 #include "base/location.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "base/run_loop.h" 17 #include "base/stl_util.h" 18 #include "base/time/time.h" 19 #include "chrome/browser/chrome_notification_types.h" 20 #include "chrome/browser/invalidation/invalidation_service_factory.h" 21 #include "chrome/browser/sessions/session_tab_helper.h" 22 #include "chrome/browser/signin/profile_oauth2_token_service.h" 23 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 24 #include "chrome/browser/signin/signin_manager.h" 25 #include "chrome/browser/signin/signin_manager_factory.h" 26 #include "chrome/browser/sync/abstract_profile_sync_service_test.h" 27 #include "chrome/browser/sync/fake_oauth2_token_service.h" 28 #include "chrome/browser/sync/glue/device_info.h" 29 #include "chrome/browser/sync/glue/session_change_processor.h" 30 #include "chrome/browser/sync/glue/session_data_type_controller.h" 31 #include "chrome/browser/sync/glue/session_model_associator.h" 32 #include "chrome/browser/sync/glue/session_sync_test_helper.h" 33 #include "chrome/browser/sync/glue/sync_backend_host.h" 34 #include "chrome/browser/sync/glue/synced_device_tracker.h" 35 #include "chrome/browser/sync/glue/synced_tab_delegate.h" 36 #include "chrome/browser/sync/glue/tab_node_pool.h" 37 #include "chrome/browser/sync/profile_sync_components_factory_mock.h" 38 #include "chrome/browser/sync/profile_sync_service_factory.h" 39 #include "chrome/browser/sync/profile_sync_test_util.h" 40 #include "chrome/browser/sync/test_profile_sync_service.h" 41 #include "chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h" 42 #include "chrome/browser/ui/tabs/tab_strip_model.h" 43 #include "chrome/test/base/browser_with_test_window_test.h" 44 #include "chrome/test/base/testing_profile.h" 45 #include "content/public/browser/navigation_controller.h" 46 #include "content/public/browser/navigation_entry.h" 47 #include "content/public/browser/notification_observer.h" 48 #include "content/public/browser/notification_registrar.h" 49 #include "content/public/browser/notification_service.h" 50 #include "content/public/browser/web_contents.h" 51 #include "content/public/test/test_browser_thread.h" 52 #include "google_apis/gaia/gaia_constants.h" 53 #include "net/url_request/test_url_fetcher_factory.h" 54 #include "sync/internal_api/public/base/model_type.h" 55 #include "sync/internal_api/public/change_record.h" 56 #include "sync/internal_api/public/read_node.h" 57 #include "sync/internal_api/public/read_transaction.h" 58 #include "sync/internal_api/public/test/test_user_share.h" 59 #include "sync/internal_api/public/write_node.h" 60 #include "sync/internal_api/public/write_transaction.h" 61 #include "sync/protocol/session_specifics.pb.h" 62 #include "sync/protocol/sync.pb.h" 63 #include "testing/gmock/include/gmock/gmock.h" 64 #include "testing/gtest/include/gtest/gtest.h" 65 #include "ui/base/ui_base_types.h" 66 #include "url/gurl.h" 67 68 using browser_sync::SessionChangeProcessor; 69 using browser_sync::SessionDataTypeController; 70 using browser_sync::SessionModelAssociator; 71 using browser_sync::SyncBackendHost; 72 using content::BrowserThread; 73 using content::WebContents; 74 using syncer::ChangeRecord; 75 using testing::_; 76 using testing::Return; 77 78 namespace browser_sync { 79 80 namespace { 81 82 class FakeProfileSyncService : public TestProfileSyncService { 83 public: 84 FakeProfileSyncService( 85 ProfileSyncComponentsFactory* factory, 86 Profile* profile, 87 SigninManagerBase* signin, 88 ProfileOAuth2TokenService* oauth2_token_service, 89 ProfileSyncService::StartBehavior behavior) 90 : TestProfileSyncService(factory, 91 profile, 92 signin, 93 oauth2_token_service, 94 behavior) {} 95 virtual ~FakeProfileSyncService() {} 96 97 virtual scoped_ptr<DeviceInfo> GetLocalDeviceInfo() const OVERRIDE { 98 return scoped_ptr<DeviceInfo>(new DeviceInfo(base::GenerateGUID(), 99 "client_name", 100 std::string(), 101 std::string(), 102 sync_pb::SyncEnums::TYPE_WIN)); 103 } 104 }; 105 106 bool CompareMemoryToString( 107 const std::string& str, 108 const scoped_refptr<base::RefCountedMemory>& mem) { 109 if (mem->size() != str.size()) 110 return false; 111 for (size_t i = 0; i <mem->size(); ++i) { 112 if (str[i] != *(mem->front() + i)) 113 return false; 114 } 115 return true; 116 } 117 118 } // namespace 119 120 class ProfileSyncServiceSessionTest 121 : public BrowserWithTestWindowTest, 122 public content::NotificationObserver { 123 public: 124 ProfileSyncServiceSessionTest() 125 : window_bounds_(0, 1, 2, 3), 126 notified_of_refresh_(false), 127 notified_of_update_(false) {} 128 ProfileSyncService* sync_service() { return sync_service_.get(); } 129 130 protected: 131 virtual TestingProfile* CreateProfile() OVERRIDE { 132 TestingProfile::Builder builder; 133 builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(), 134 FakeOAuth2TokenService::BuildTokenService); 135 // Don't want the profile to create a real ProfileSyncService. 136 builder.AddTestingFactory(ProfileSyncServiceFactory::GetInstance(), NULL); 137 scoped_ptr<TestingProfile> profile(builder.Build()); 138 invalidation::InvalidationServiceFactory::GetInstance()-> 139 SetBuildOnlyFakeInvalidatorsForTest(true); 140 return profile.release(); 141 } 142 143 virtual void SetUp() { 144 // BrowserWithTestWindowTest implementation. 145 BrowserWithTestWindowTest::SetUp(); 146 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 147 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED, 148 content::NotificationService::AllSources()); 149 registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL, 150 content::NotificationService::AllSources()); 151 } 152 153 virtual void Observe(int type, 154 const content::NotificationSource& source, 155 const content::NotificationDetails& details) OVERRIDE { 156 switch (type) { 157 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED: 158 notified_of_update_ = true; 159 break; 160 case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL: 161 notified_of_refresh_ = true; 162 break; 163 default: 164 NOTREACHED(); 165 break; 166 } 167 } 168 169 virtual void TearDown() { 170 sync_service_->Shutdown(); 171 sync_service_.reset(); 172 173 // We need to destroy the profile before shutting down the threads, because 174 // some of the ref counted objects in the profile depend on their 175 // destruction on the io thread. 176 DestroyBrowserAndProfile(); 177 ASSERT_FALSE(profile()); 178 179 // Pump messages posted by the sync core thread (which may end up 180 // posting on the IO thread). 181 base::RunLoop().RunUntilIdle(); 182 BrowserWithTestWindowTest::TearDown(); 183 } 184 185 bool StartSyncService(const base::Closure& callback, 186 bool will_fail_association) { 187 if (sync_service_) 188 return false; 189 SigninManagerBase* signin = 190 SigninManagerFactory::GetForProfile(profile()); 191 signin->SetAuthenticatedUsername("test_user"); 192 ProfileOAuth2TokenService* oauth2_token_service = 193 ProfileOAuth2TokenServiceFactory::GetForProfile(profile()); 194 ProfileSyncComponentsFactoryMock* factory = 195 new ProfileSyncComponentsFactoryMock(); 196 sync_service_.reset(new FakeProfileSyncService( 197 factory, 198 profile(), 199 signin, 200 oauth2_token_service, 201 ProfileSyncService::AUTO_START)); 202 sync_service_->set_backend_init_callback(callback); 203 204 // Register the session data type. 205 SessionDataTypeController *dtc = new SessionDataTypeController(factory, 206 profile(), 207 sync_service_.get()); 208 sync_service_->RegisterDataTypeController(dtc); 209 210 model_associator_ = 211 new SessionModelAssociator(sync_service_.get(), 212 true /* setup_for_test */); 213 change_processor_ = new SessionChangeProcessor( 214 dtc, model_associator_, 215 true /* setup_for_test */); 216 EXPECT_CALL(*factory, CreateSessionSyncComponents(_, _)). 217 WillOnce(Return(ProfileSyncComponentsFactory::SyncComponents( 218 model_associator_, change_processor_))); 219 EXPECT_CALL(*factory, CreateDataTypeManager(_, _, _, _, _, _)). 220 WillOnce(ReturnNewDataTypeManager()); 221 222 ProfileOAuth2TokenServiceFactory::GetForProfile(profile()) 223 ->UpdateCredentials("test_user", "oauth2_login_token"); 224 sync_service_->Initialize(); 225 base::MessageLoop::current()->Run(); 226 return true; 227 } 228 229 // Path used in testing. 230 base::ScopedTempDir temp_dir_; 231 SessionModelAssociator* model_associator_; 232 SessionChangeProcessor* change_processor_; 233 SessionID window_id_; 234 scoped_ptr<TestProfileSyncService> sync_service_; 235 const gfx::Rect window_bounds_; 236 bool notified_of_refresh_; 237 bool notified_of_update_; 238 content::NotificationRegistrar registrar_; 239 net::TestURLFetcherFactory fetcher_factory_; 240 SessionSyncTestHelper helper_; 241 }; 242 243 class CreateRootHelper { 244 public: 245 explicit CreateRootHelper(ProfileSyncServiceSessionTest* test) 246 : callback_(base::Bind(&CreateRootHelper::CreateRootCallback, 247 base::Unretained(this), test)), 248 success_(false) { 249 } 250 251 virtual ~CreateRootHelper() {} 252 253 const base::Closure& callback() const { return callback_; } 254 bool success() { return success_; } 255 256 private: 257 void CreateRootCallback(ProfileSyncServiceSessionTest* test) { 258 success_ = syncer::TestUserShare::CreateRoot( 259 syncer::SESSIONS, test->sync_service()->GetUserShare()); 260 } 261 262 base::Closure callback_; 263 bool success_; 264 }; 265 266 // Test that we can write this machine's session to a node and retrieve it. 267 TEST_F(ProfileSyncServiceSessionTest, WriteSessionToNode) { 268 CreateRootHelper create_root(this); 269 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 270 ASSERT_TRUE(create_root.success()); 271 272 // Check that the DataTypeController associated the models. 273 bool has_nodes; 274 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes)); 275 ASSERT_TRUE(has_nodes); 276 std::string machine_tag = model_associator_->GetCurrentMachineTag(); 277 int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag); 278 ASSERT_NE(syncer::kInvalidId, sync_id); 279 280 // Check that we can get the correct session specifics back from the node. 281 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 282 syncer::ReadNode node(&trans); 283 ASSERT_EQ(syncer::BaseNode::INIT_OK, 284 node.InitByClientTagLookup(syncer::SESSIONS, machine_tag)); 285 const sync_pb::SessionSpecifics& specifics(node.GetSessionSpecifics()); 286 ASSERT_EQ(machine_tag, specifics.session_tag()); 287 ASSERT_TRUE(specifics.has_header()); 288 const sync_pb::SessionHeader& header_s = specifics.header(); 289 ASSERT_TRUE(header_s.has_device_type()); 290 ASSERT_EQ("client_name", header_s.client_name()); 291 ASSERT_EQ(0, header_s.window_size()); 292 } 293 294 // Crashes sometimes on Windows, particularly XP. 295 // See http://crbug.com/174951 296 #if defined(OS_WIN) 297 #define MAYBE_WriteFilledSessionToNode DISABLED_WriteFilledSessionToNode 298 #else 299 #define MAYBE_WriteFilledSessionToNode WriteFilledSessionToNode 300 #endif // defined(OS_WIN) 301 302 // Test that we can fill this machine's session, write it to a node, 303 // and then retrieve it. 304 TEST_F(ProfileSyncServiceSessionTest, MAYBE_WriteFilledSessionToNode) { 305 CreateRootHelper create_root(this); 306 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 307 ASSERT_TRUE(create_root.success()); 308 309 // Check that the DataTypeController associated the models. 310 bool has_nodes; 311 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes)); 312 ASSERT_TRUE(has_nodes); 313 AddTab(browser(), GURL("http://foo/1")); 314 NavigateAndCommitActiveTab(GURL("http://foo/2")); 315 AddTab(browser(), GURL("http://bar/1")); 316 NavigateAndCommitActiveTab(GURL("http://bar/2")); 317 318 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes)); 319 ASSERT_TRUE(has_nodes); 320 std::string machine_tag = model_associator_->GetCurrentMachineTag(); 321 int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag); 322 ASSERT_NE(syncer::kInvalidId, sync_id); 323 324 // Check that this machine's data is not included in the foreign windows. 325 std::vector<const SyncedSession*> foreign_sessions; 326 ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 327 ASSERT_EQ(foreign_sessions.size(), 0U); 328 329 // Get the tabs for this machine from the node and check that they were 330 // filled. 331 SessionModelAssociator::TabLinksMap tab_map = 332 model_associator_->local_tab_map_; 333 ASSERT_EQ(2U, tab_map.size()); 334 // Tabs are ordered by sessionid in tab_map, so should be able to traverse 335 // the tree based on order of tabs created 336 SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin(); 337 ASSERT_EQ(2, iter->second->tab()->GetEntryCount()); 338 ASSERT_EQ(GURL("http://foo/1"), iter->second->tab()-> 339 GetEntryAtIndex(0)->GetVirtualURL()); 340 ASSERT_EQ(GURL("http://foo/2"), iter->second->tab()-> 341 GetEntryAtIndex(1)->GetVirtualURL()); 342 iter++; 343 ASSERT_EQ(2, iter->second->tab()->GetEntryCount()); 344 ASSERT_EQ(GURL("http://bar/1"), iter->second->tab()-> 345 GetEntryAtIndex(0)->GetVirtualURL()); 346 ASSERT_EQ(GURL("http://bar/2"), iter->second->tab()-> 347 GetEntryAtIndex(1)->GetVirtualURL()); 348 } 349 350 // Test that we fail on a failed model association. 351 TEST_F(ProfileSyncServiceSessionTest, FailModelAssociation) { 352 ASSERT_TRUE(StartSyncService(base::Closure(), true)); 353 ASSERT_TRUE(sync_service_->HasUnrecoverableError()); 354 } 355 356 // Write a foreign session to a node, and then retrieve it. 357 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) { 358 CreateRootHelper create_root(this); 359 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 360 ASSERT_TRUE(create_root.success()); 361 362 // Check that the DataTypeController associated the models. 363 bool has_nodes; 364 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes)); 365 ASSERT_TRUE(has_nodes); 366 367 // Fill an instance of session specifics with a foreign session's data. 368 std::string tag = "tag1"; 369 SessionID::id_type nums1[] = {5, 10, 13, 17}; 370 std::vector<sync_pb::SessionSpecifics> tabs1; 371 std::vector<SessionID::id_type> tab_list1(nums1, nums1 + arraysize(nums1)); 372 sync_pb::SessionSpecifics meta(helper_.BuildForeignSession( 373 tag, tab_list1, &tabs1)); 374 375 // Update associator with the session's meta node containing one window. 376 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 377 // Add tabs for the window. 378 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin(); 379 iter != tabs1.end(); ++iter) { 380 model_associator_->AssociateForeignSpecifics(*iter, base::Time()); 381 } 382 383 // Check that the foreign session was associated and retrieve the data. 384 std::vector<const SyncedSession*> foreign_sessions; 385 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 386 ASSERT_EQ(1U, foreign_sessions.size()); 387 std::vector<std::vector<SessionID::id_type> > session_reference; 388 session_reference.push_back(tab_list1); 389 helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 390 } 391 392 // Write a foreign session with one window to a node. Sync, then add a window. 393 // Sync, then add a third window. Close the two windows. 394 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeThreeWindows) { 395 CreateRootHelper create_root(this); 396 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 397 ASSERT_TRUE(create_root.success()); 398 399 // Build a foreign session with one window and four tabs. 400 std::string tag = "tag1"; 401 SessionID::id_type nums1[] = {5, 10, 13, 17}; 402 std::vector<sync_pb::SessionSpecifics> tabs1; 403 std::vector<SessionID::id_type> tab_list1(nums1, nums1 + arraysize(nums1)); 404 sync_pb::SessionSpecifics meta(helper_.BuildForeignSession( 405 tag, tab_list1, &tabs1)); 406 // Update associator with the session's meta node containing one window. 407 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 408 // Add tabs for first window. 409 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin(); 410 iter != tabs1.end(); ++iter) { 411 model_associator_->AssociateForeignSpecifics(*iter, base::Time()); 412 } 413 414 // Verify first window 415 std::vector<const SyncedSession*> foreign_sessions; 416 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 417 std::vector<std::vector<SessionID::id_type> > session_reference; 418 session_reference.push_back(tab_list1); 419 helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 420 421 // Add a second window. 422 SessionID::id_type tab_nums2[] = {7, 15, 18, 20}; 423 std::vector<SessionID::id_type> tab_list2( 424 tab_nums2, tab_nums2 + arraysize(tab_nums2)); 425 helper_.AddWindowSpecifics(1, tab_list2, &meta); 426 std::vector<sync_pb::SessionSpecifics> tabs2; 427 tabs2.resize(tab_list2.size()); 428 for (size_t i = 0; i < tab_list2.size(); ++i) { 429 helper_.BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]); 430 } 431 // Update associator with the session's meta node containing two windows. 432 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 433 // Add tabs for second window. 434 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs2.begin(); 435 iter != tabs2.end(); ++iter) { 436 model_associator_->AssociateForeignSpecifics(*iter, base::Time()); 437 } 438 439 // Verify the two windows. 440 foreign_sessions.clear(); 441 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 442 ASSERT_EQ(1U, foreign_sessions.size()); 443 session_reference.push_back(tab_list2); 444 helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 445 446 // Add a third window. 447 SessionID::id_type tab_nums3[] = {8, 16, 19, 21}; 448 std::vector<SessionID::id_type> tab_list3( 449 tab_nums3, tab_nums3 + arraysize(tab_nums3)); 450 helper_.AddWindowSpecifics(2, tab_list3, &meta); 451 std::vector<sync_pb::SessionSpecifics> tabs3; 452 tabs3.resize(tab_list3.size()); 453 for (size_t i = 0; i < tab_list3.size(); ++i) { 454 helper_.BuildTabSpecifics(tag, 0, tab_list3[i], &tabs3[i]); 455 } 456 // Update associator with the session's meta node containing three windows. 457 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 458 // Add tabs for third window. 459 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs3.begin(); 460 iter != tabs3.end(); ++iter) { 461 model_associator_->AssociateForeignSpecifics(*iter, base::Time()); 462 } 463 464 // Verify the three windows 465 foreign_sessions.clear(); 466 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 467 ASSERT_EQ(1U, foreign_sessions.size()); 468 session_reference.push_back(tab_list3); 469 helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 470 471 // Close third window (by clearing and then not adding it back). 472 meta.mutable_header()->clear_window(); 473 helper_.AddWindowSpecifics(0, tab_list1, &meta); 474 helper_.AddWindowSpecifics(1, tab_list2, &meta); 475 // Update associator with just the meta node, now containing only two windows. 476 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 477 478 // Verify first two windows are still there. 479 foreign_sessions.clear(); 480 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 481 ASSERT_EQ(1U, foreign_sessions.size()); 482 session_reference.pop_back(); // Pop off the data for the third window. 483 helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 484 485 // Close second window (by clearing and then not adding it back). 486 meta.mutable_header()->clear_window(); 487 helper_.AddWindowSpecifics(0, tab_list1, &meta); 488 // Update associator with just the meta node, now containing only one windows. 489 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 490 491 // Verify first window is still there. 492 foreign_sessions.clear(); 493 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 494 ASSERT_EQ(1U, foreign_sessions.size()); 495 session_reference.pop_back(); // Pop off the data for the second window. 496 helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 497 } 498 499 // Write a foreign session to a node, with the tabs arriving first, and then 500 // retrieve it. 501 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeTabsFirst) { 502 CreateRootHelper create_root(this); 503 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 504 ASSERT_TRUE(create_root.success()); 505 506 // Fill an instance of session specifics with a foreign session's data. 507 std::string tag = "tag1"; 508 SessionID::id_type nums1[] = {5, 10, 13, 17}; 509 std::vector<sync_pb::SessionSpecifics> tabs1; 510 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1)); 511 sync_pb::SessionSpecifics meta(helper_.BuildForeignSession( 512 tag, tab_list1, &tabs1)); 513 514 // Add tabs for first window. 515 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin(); 516 iter != tabs1.end(); ++iter) { 517 model_associator_->AssociateForeignSpecifics(*iter, base::Time()); 518 } 519 // Update associator with the session's meta node containing one window. 520 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 521 522 // Check that the foreign session was associated and retrieve the data. 523 std::vector<const SyncedSession*> foreign_sessions; 524 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 525 ASSERT_EQ(1U, foreign_sessions.size()); 526 std::vector<std::vector<SessionID::id_type> > session_reference; 527 session_reference.push_back(tab_list1); 528 helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 529 } 530 531 // Write a foreign session to a node with some tabs that never arrive. 532 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeMissingTabs) { 533 CreateRootHelper create_root(this); 534 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 535 ASSERT_TRUE(create_root.success()); 536 537 // Fill an instance of session specifics with a foreign session's data. 538 std::string tag = "tag1"; 539 SessionID::id_type nums1[] = {5, 10, 13, 17}; 540 std::vector<sync_pb::SessionSpecifics> tabs1; 541 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1)); 542 sync_pb::SessionSpecifics meta(helper_.BuildForeignSession( 543 tag, tab_list1, &tabs1)); 544 // Add a second window, but this time only create two tab nodes, despite the 545 // window expecting four tabs. 546 SessionID::id_type tab_nums2[] = {7, 15, 18, 20}; 547 std::vector<SessionID::id_type> tab_list2( 548 tab_nums2, tab_nums2 + arraysize(tab_nums2)); 549 helper_.AddWindowSpecifics(1, tab_list2, &meta); 550 std::vector<sync_pb::SessionSpecifics> tabs2; 551 tabs2.resize(2); 552 for (size_t i = 0; i < 2; ++i) { 553 helper_.BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]); 554 } 555 556 // Update associator with the session's meta node containing two windows. 557 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 558 // Add tabs for first window. 559 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin(); 560 iter != tabs1.end(); ++iter) { 561 model_associator_->AssociateForeignSpecifics(*iter, base::Time()); 562 } 563 // Add tabs for second window. 564 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs2.begin(); 565 iter != tabs2.end(); ++iter) { 566 model_associator_->AssociateForeignSpecifics(*iter, base::Time()); 567 } 568 569 // Check that the foreign session was associated and retrieve the data. 570 std::vector<const SyncedSession*> foreign_sessions; 571 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 572 ASSERT_EQ(1U, foreign_sessions.size()); 573 ASSERT_EQ(2U, foreign_sessions[0]->windows.size()); 574 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size()); 575 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size()); 576 577 // Close the second window. 578 meta.mutable_header()->clear_window(); 579 helper_.AddWindowSpecifics(0, tab_list1, &meta); 580 581 // Update associator with the session's meta node containing one window. 582 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 583 584 // Check that the foreign session was associated and retrieve the data. 585 foreign_sessions.clear(); 586 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 587 ASSERT_EQ(1U, foreign_sessions.size()); 588 ASSERT_EQ(1U, foreign_sessions[0]->windows.size()); 589 std::vector<std::vector<SessionID::id_type> > session_reference; 590 session_reference.push_back(tab_list1); 591 helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 592 } 593 594 // Test the DataTypeController on update. 595 TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionUpdate) { 596 CreateRootHelper create_root(this); 597 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 598 ASSERT_TRUE(create_root.success()); 599 int64 node_id = model_associator_->GetSyncIdFromSessionTag( 600 model_associator_->GetCurrentMachineTag()); 601 ASSERT_FALSE(notified_of_update_); 602 { 603 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 604 change_processor_->ApplyChangesFromSyncModel( 605 &trans, 0, 606 ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList( 607 node_id, ChangeRecord::ACTION_UPDATE)); 608 } 609 ASSERT_TRUE(notified_of_update_); 610 } 611 612 // Test the DataTypeController on add. 613 TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionAdd) { 614 CreateRootHelper create_root(this); 615 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 616 ASSERT_TRUE(create_root.success()); 617 618 int64 node_id = model_associator_->GetSyncIdFromSessionTag( 619 model_associator_->GetCurrentMachineTag()); 620 ASSERT_FALSE(notified_of_update_); 621 { 622 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 623 change_processor_->ApplyChangesFromSyncModel( 624 &trans, 0, 625 ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList( 626 node_id, ChangeRecord::ACTION_ADD)); 627 } 628 ASSERT_TRUE(notified_of_update_); 629 } 630 631 // Test the DataTypeController on delete. 632 TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionDelete) { 633 CreateRootHelper create_root(this); 634 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 635 ASSERT_TRUE(create_root.success()); 636 637 int64 node_id = model_associator_->GetSyncIdFromSessionTag( 638 model_associator_->GetCurrentMachineTag()); 639 sync_pb::EntitySpecifics deleted_specifics; 640 deleted_specifics.mutable_session()->set_session_tag("tag"); 641 ASSERT_FALSE(notified_of_update_); 642 { 643 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 644 change_processor_->ApplyChangesFromSyncModel( 645 &trans, 0, 646 ProfileSyncServiceTestHelper::MakeSingletonDeletionChangeRecordList( 647 node_id, deleted_specifics)); 648 } 649 ASSERT_TRUE(notified_of_update_); 650 } 651 // Test the TabNodePool when it starts off empty. 652 TEST_F(ProfileSyncServiceSessionTest, TabNodePoolEmpty) { 653 CreateRootHelper create_root(this); 654 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 655 ASSERT_TRUE(create_root.success()); 656 657 std::vector<int> node_ids; 658 ASSERT_EQ(0U, model_associator_->local_tab_pool_.Capacity()); 659 ASSERT_TRUE(model_associator_->local_tab_pool_.Empty()); 660 ASSERT_TRUE(model_associator_->local_tab_pool_.Full()); 661 const size_t num_ids = 10; 662 for (size_t i = 0; i < num_ids; ++i) { 663 int id = model_associator_->local_tab_pool_.GetFreeTabNode(); 664 ASSERT_GT(id, TabNodePool::kInvalidTabNodeID); 665 node_ids.push_back(id); 666 // Associate with a tab node. 667 model_associator_->local_tab_pool_.AssociateTabNode(id, i + 1); 668 } 669 ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity()); 670 ASSERT_TRUE(model_associator_->local_tab_pool_.Empty()); 671 ASSERT_FALSE(model_associator_->local_tab_pool_.Full()); 672 for (size_t i = 0; i < num_ids; ++i) { 673 model_associator_->local_tab_pool_.FreeTabNode(node_ids[i]); 674 } 675 ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity()); 676 ASSERT_FALSE(model_associator_->local_tab_pool_.Empty()); 677 ASSERT_TRUE(model_associator_->local_tab_pool_.Full()); 678 } 679 680 // TODO(jhorwich): Re-enable when crbug.com/121487 addressed 681 TEST_F(ProfileSyncServiceSessionTest, TabNodePoolNonEmpty) { 682 CreateRootHelper create_root(this); 683 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 684 ASSERT_TRUE(create_root.success()); 685 686 const size_t num_starting_nodes = 3; 687 for (size_t i = 0; i < num_starting_nodes; ++i) { 688 size_t node_id = i + 1; 689 model_associator_->local_tab_pool_.AddTabNode(node_id); 690 model_associator_->local_tab_pool_.AssociateTabNode(node_id, i); 691 model_associator_->local_tab_pool_.FreeTabNode(node_id); 692 } 693 694 std::vector<int> node_ids; 695 ASSERT_EQ(num_starting_nodes, model_associator_->local_tab_pool_.Capacity()); 696 ASSERT_FALSE(model_associator_->local_tab_pool_.Empty()); 697 ASSERT_TRUE(model_associator_->local_tab_pool_.Full()); 698 const size_t num_ids = 10; 699 for (size_t i = 0; i < num_ids; ++i) { 700 int id = model_associator_->local_tab_pool_.GetFreeTabNode(); 701 ASSERT_GT(id, TabNodePool::kInvalidTabNodeID); 702 node_ids.push_back(id); 703 // Associate with a tab node. 704 model_associator_->local_tab_pool_.AssociateTabNode(id, i + 1); 705 } 706 ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity()); 707 ASSERT_TRUE(model_associator_->local_tab_pool_.Empty()); 708 ASSERT_FALSE(model_associator_->local_tab_pool_.Full()); 709 for (size_t i = 0; i < num_ids; ++i) { 710 model_associator_->local_tab_pool_.FreeTabNode(node_ids[i]); 711 } 712 ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity()); 713 ASSERT_FALSE(model_associator_->local_tab_pool_.Empty()); 714 ASSERT_TRUE(model_associator_->local_tab_pool_.Full()); 715 } 716 717 // Write a foreign session to a node, and then delete it. 718 TEST_F(ProfileSyncServiceSessionTest, DeleteForeignSession) { 719 CreateRootHelper create_root(this); 720 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 721 ASSERT_TRUE(create_root.success()); 722 723 // Check that the DataTypeController associated the models. 724 bool has_nodes; 725 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes)); 726 ASSERT_TRUE(has_nodes); 727 728 // A foreign session's tag. 729 std::string tag = "tag1"; 730 731 // Should do nothing if the foreign session doesn't exist. 732 std::vector<const SyncedSession*> foreign_sessions; 733 ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 734 ASSERT_FALSE(notified_of_update_); 735 model_associator_->DeleteForeignSession(tag); 736 ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 737 // Verify that deleteForeignSession did not trigger the 738 // NOTIFICATION_FOREIGN_SESSION_DISABLED notification. 739 ASSERT_FALSE(notified_of_update_); 740 741 // Fill an instance of session specifics with a foreign session's data. 742 SessionID::id_type nums1[] = {5, 10, 13, 17}; 743 std::vector<sync_pb::SessionSpecifics> tabs1; 744 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1)); 745 sync_pb::SessionSpecifics meta(helper_.BuildForeignSession( 746 tag, tab_list1, &tabs1)); 747 748 // Update associator with the session's meta node containing one window. 749 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 750 // Add tabs for the window. 751 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin(); 752 iter != tabs1.end(); ++iter) { 753 model_associator_->AssociateForeignSpecifics(*iter, base::Time()); 754 } 755 756 // Check that the foreign session was associated and retrieve the data. 757 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 758 ASSERT_EQ(1U, foreign_sessions.size()); 759 std::vector<std::vector<SessionID::id_type> > session_reference; 760 session_reference.push_back(tab_list1); 761 helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 762 763 ASSERT_FALSE(notified_of_update_); 764 // Now delete the foreign session. 765 model_associator_->DeleteForeignSession(tag); 766 ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 767 768 // Verify that deleteForeignSession triggers the 769 // NOTIFICATION_FOREIGN_SESSION_DISABLED notification. 770 ASSERT_TRUE(notified_of_update_); 771 } 772 773 // Associate both a non-stale foreign session and a stale foreign session. 774 // Ensure only the stale session gets deleted. 775 TEST_F(ProfileSyncServiceSessionTest, DeleteStaleSessions) { 776 CreateRootHelper create_root(this); 777 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 778 ASSERT_TRUE(create_root.success()); 779 780 // Fill two instances of session specifics with a foreign session's data. 781 std::string tag = "tag1"; 782 SessionID::id_type nums1[] = {5, 10, 13, 17}; 783 std::vector<sync_pb::SessionSpecifics> tabs1; 784 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1)); 785 sync_pb::SessionSpecifics meta(helper_.BuildForeignSession( 786 tag, tab_list1, &tabs1)); 787 std::string tag2 = "tag2"; 788 sync_pb::SessionSpecifics meta2; 789 helper_.BuildSessionSpecifics(tag2, &meta2); 790 SessionID::id_type tab_nums2[] = {8, 15, 18, 20}; 791 std::vector<SessionID::id_type> tab_list2( 792 tab_nums2, tab_nums2 + arraysize(tab_nums2)); 793 helper_.AddWindowSpecifics(0, tab_list2, &meta2); 794 std::vector<sync_pb::SessionSpecifics> tabs2; 795 tabs2.resize(tab_list2.size()); 796 for (size_t i = 0; i < tab_list2.size(); ++i) { 797 helper_.BuildTabSpecifics(tag2, 0, tab_list2[i], &tabs2[i]); 798 } 799 800 // Set the modification time for tag1 to be 21 days ago, tag2 to 5 days ago. 801 base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21); 802 base::Time tag2_time = base::Time::Now() - base::TimeDelta::FromDays(5); 803 804 // Associate specifics. 805 model_associator_->AssociateForeignSpecifics(meta, tag1_time); 806 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin(); 807 iter != tabs1.end(); ++iter) { 808 model_associator_->AssociateForeignSpecifics(*iter, tag1_time); 809 } 810 model_associator_->AssociateForeignSpecifics(meta2, tag2_time); 811 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs2.begin(); 812 iter != tabs2.end(); ++iter) { 813 model_associator_->AssociateForeignSpecifics(*iter, tag2_time); 814 } 815 816 // Check that the foreign session was associated and retrieve the data. 817 std::vector<const SyncedSession*> foreign_sessions; 818 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 819 ASSERT_EQ(2U, foreign_sessions.size()); 820 821 // Now delete the stale session and verify the non-stale one is still there. 822 model_associator_->DeleteStaleSessions(); 823 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 824 ASSERT_EQ(1U, foreign_sessions.size()); 825 std::vector<std::vector<SessionID::id_type> > session_reference; 826 session_reference.push_back(tab_list2); 827 helper_.VerifySyncedSession(tag2, session_reference, *(foreign_sessions[0])); 828 } 829 830 // Write a stale foreign session to a node. Then update one of its tabs so 831 // the session is no longer stale. Ensure it doesn't get deleted. 832 TEST_F(ProfileSyncServiceSessionTest, StaleSessionRefresh) { 833 CreateRootHelper create_root(this); 834 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 835 ASSERT_TRUE(create_root.success()); 836 837 std::string tag = "tag1"; 838 SessionID::id_type nums1[] = {5, 10, 13, 17}; 839 std::vector<sync_pb::SessionSpecifics> tabs1; 840 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1)); 841 sync_pb::SessionSpecifics meta(helper_.BuildForeignSession( 842 tag, tab_list1, &tabs1)); 843 844 // Associate. 845 base::Time stale_time = base::Time::Now() - base::TimeDelta::FromDays(21); 846 model_associator_->AssociateForeignSpecifics(meta, stale_time); 847 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin(); 848 iter != tabs1.end(); ++iter) { 849 model_associator_->AssociateForeignSpecifics(*iter, stale_time); 850 } 851 852 // Associate one of the tabs with a non-stale time. 853 model_associator_->AssociateForeignSpecifics(tabs1[0], base::Time::Now()); 854 855 // Check that the foreign session was associated and retrieve the data. 856 std::vector<const SyncedSession*> foreign_sessions; 857 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 858 ASSERT_EQ(1U, foreign_sessions.size()); 859 860 // Verify the now non-stale session does not get deleted. 861 model_associator_->DeleteStaleSessions(); 862 ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 863 ASSERT_EQ(1U, foreign_sessions.size()); 864 std::vector<std::vector<SessionID::id_type> > session_reference; 865 session_reference.push_back(tab_list1); 866 helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 867 } 868 869 // Crashes sometimes on Windows, particularly XP. 870 // See http://crbug.com/174951 871 #if defined(OS_WIN) 872 #define MAYBE_ValidTabs DISABLED_ValidTabs 873 #else 874 #define MAYBE_ValidTabs ValidTabs 875 #endif // defined(OS_WIN) 876 877 // Test that tabs with nothing but "chrome://*" and "file://*" navigations are 878 // not be synced. 879 TEST_F(ProfileSyncServiceSessionTest, MAYBE_ValidTabs) { 880 CreateRootHelper create_root(this); 881 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 882 ASSERT_TRUE(create_root.success()); 883 884 AddTab(browser(), GURL("chrome://bla1/")); 885 NavigateAndCommitActiveTab(GURL("chrome://bla2")); 886 AddTab(browser(), GURL("file://bla3/")); 887 AddTab(browser(), GURL("bla://bla")); 888 // Note: chrome://newtab has special handling which crashes in unit tests. 889 890 // Get the tabs for this machine. Only the bla:// url should be synced. 891 SessionModelAssociator::TabLinksMap tab_map = 892 model_associator_->local_tab_map_; 893 ASSERT_EQ(1U, tab_map.size()); 894 SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin(); 895 ASSERT_EQ(1, iter->second->tab()->GetEntryCount()); 896 ASSERT_EQ(GURL("bla://bla"), iter->second->tab()-> 897 GetEntryAtIndex(0)->GetVirtualURL()); 898 } 899 900 // Verify that AttemptSessionsDataRefresh triggers the 901 // NOTIFICATION_SYNC_REFRESH_LOCAL notification. 902 // TODO(zea): Once we can have unit tests that are able to open to the NTP, 903 // test that the NTP/#opentabs URL triggers a refresh as well (but only when 904 // it is the active tab). 905 TEST_F(ProfileSyncServiceSessionTest, SessionsRefresh) { 906 CreateRootHelper create_root(this); 907 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 908 ASSERT_TRUE(create_root.success()); 909 910 // Empty, so returns false. 911 std::vector<const SyncedSession*> foreign_sessions; 912 ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 913 ASSERT_FALSE(notified_of_refresh_); 914 model_associator_->AttemptSessionsDataRefresh(); 915 ASSERT_TRUE(notified_of_refresh_); 916 917 // Nothing should have changed since we don't have unapplied data. 918 ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 919 } 920 921 // Ensure model association associates the pre-existing tabs. 922 TEST_F(ProfileSyncServiceSessionTest, ExistingTabs) { 923 AddTab(browser(), GURL("http://foo1")); 924 NavigateAndCommitActiveTab(GURL("http://foo2")); 925 AddTab(browser(), GURL("http://bar1")); 926 NavigateAndCommitActiveTab(GURL("http://bar2")); 927 928 CreateRootHelper create_root(this); 929 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 930 ASSERT_TRUE(create_root.success()); 931 bool has_nodes; 932 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes)); 933 ASSERT_TRUE(has_nodes); 934 935 std::string machine_tag = model_associator_->GetCurrentMachineTag(); 936 int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag); 937 ASSERT_NE(syncer::kInvalidId, sync_id); 938 939 // Check that this machine's data is not included in the foreign windows. 940 std::vector<const SyncedSession*> foreign_sessions; 941 ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions)); 942 ASSERT_EQ(foreign_sessions.size(), 0U); 943 944 // Get the tabs for this machine from the node and check that they were 945 // filled. 946 SessionModelAssociator::TabLinksMap tab_map = 947 model_associator_->local_tab_map_; 948 ASSERT_EQ(2U, tab_map.size()); 949 // Tabs are ordered by sessionid in tab_map, so should be able to traverse 950 // the tree based on order of tabs created 951 SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin(); 952 ASSERT_EQ(2, iter->second->tab()->GetEntryCount()); 953 ASSERT_EQ(GURL("http://foo1"), iter->second->tab()-> 954 GetEntryAtIndex(0)->GetVirtualURL()); 955 ASSERT_EQ(GURL("http://foo2"), iter->second->tab()-> 956 GetEntryAtIndex(1)->GetVirtualURL()); 957 iter++; 958 ASSERT_EQ(2, iter->second->tab()->GetEntryCount()); 959 ASSERT_EQ(GURL("http://bar1"), iter->second->tab()-> 960 GetEntryAtIndex(0)->GetVirtualURL()); 961 ASSERT_EQ(GURL("http://bar2"), iter->second->tab()-> 962 GetEntryAtIndex(1)->GetVirtualURL()); 963 } 964 965 TEST_F(ProfileSyncServiceSessionTest, MissingHeaderAndTab) { 966 AddTab(browser(), GURL("http://foo1")); 967 NavigateAndCommitActiveTab(GURL("http://foo2")); 968 AddTab(browser(), GURL("http://bar1")); 969 NavigateAndCommitActiveTab(GURL("http://bar2")); 970 CreateRootHelper create_root(this); 971 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 972 syncer::SyncError error; 973 std::string local_tag = model_associator_->GetCurrentMachineTag(); 974 975 error = model_associator_->DisassociateModels(); 976 ASSERT_FALSE(error.IsSet()); 977 { 978 // Create a sync node with the local tag but neither header nor tab field. 979 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 980 syncer::ReadNode root(&trans); 981 root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS)); 982 syncer::WriteNode extra_header(&trans); 983 syncer::WriteNode::InitUniqueByCreationResult result = 984 extra_header.InitUniqueByCreation(syncer::SESSIONS, root, "new_tag"); 985 ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result); 986 sync_pb::SessionSpecifics specifics; 987 specifics.set_session_tag(local_tag); 988 extra_header.SetSessionSpecifics(specifics); 989 } 990 991 error = model_associator_->AssociateModels(NULL, NULL); 992 ASSERT_FALSE(error.IsSet()); 993 } 994 995 TEST_F(ProfileSyncServiceSessionTest, MultipleHeaders) { 996 AddTab(browser(), GURL("http://foo1")); 997 NavigateAndCommitActiveTab(GURL("http://foo2")); 998 AddTab(browser(), GURL("http://bar1")); 999 NavigateAndCommitActiveTab(GURL("http://bar2")); 1000 CreateRootHelper create_root(this); 1001 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 1002 syncer::SyncError error; 1003 std::string local_tag = model_associator_->GetCurrentMachineTag(); 1004 1005 error = model_associator_->DisassociateModels(); 1006 ASSERT_FALSE(error.IsSet()); 1007 { 1008 // Create another sync node with a header field and the local tag. 1009 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 1010 syncer::ReadNode root(&trans); 1011 root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS)); 1012 syncer::WriteNode extra_header(&trans); 1013 syncer::WriteNode::InitUniqueByCreationResult result = 1014 extra_header.InitUniqueByCreation(syncer::SESSIONS, 1015 root, local_tag + "_"); 1016 ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result); 1017 sync_pb::SessionSpecifics specifics; 1018 specifics.set_session_tag(local_tag); 1019 specifics.mutable_header(); 1020 extra_header.SetSessionSpecifics(specifics); 1021 } 1022 error = model_associator_->AssociateModels(NULL, NULL); 1023 ASSERT_FALSE(error.IsSet()); 1024 } 1025 1026 TEST_F(ProfileSyncServiceSessionTest, CorruptedForeign) { 1027 AddTab(browser(), GURL("http://foo1")); 1028 NavigateAndCommitActiveTab(GURL("http://foo2")); 1029 AddTab(browser(), GURL("http://bar1")); 1030 NavigateAndCommitActiveTab(GURL("http://bar2")); 1031 CreateRootHelper create_root(this); 1032 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 1033 syncer::SyncError error; 1034 1035 error = model_associator_->DisassociateModels(); 1036 ASSERT_FALSE(error.IsSet()); 1037 { 1038 // Create another sync node with neither header nor tab field and a foreign 1039 // tag. 1040 std::string foreign_tag = "foreign_tag"; 1041 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 1042 syncer::ReadNode root(&trans); 1043 root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS)); 1044 syncer::WriteNode extra_header(&trans); 1045 syncer::WriteNode::InitUniqueByCreationResult result = 1046 extra_header.InitUniqueByCreation(syncer::SESSIONS, 1047 root, foreign_tag); 1048 ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result); 1049 sync_pb::SessionSpecifics specifics; 1050 specifics.set_session_tag(foreign_tag); 1051 extra_header.SetSessionSpecifics(specifics); 1052 } 1053 error = model_associator_->AssociateModels(NULL, NULL); 1054 ASSERT_FALSE(error.IsSet()); 1055 } 1056 1057 TEST_F(ProfileSyncServiceSessionTest, MissingLocalTabNode) { 1058 AddTab(browser(), GURL("http://foo1")); 1059 NavigateAndCommitActiveTab(GURL("http://foo2")); 1060 AddTab(browser(), GURL("http://bar1")); 1061 NavigateAndCommitActiveTab(GURL("http://bar2")); 1062 CreateRootHelper create_root(this); 1063 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 1064 std::string local_tag = model_associator_->GetCurrentMachineTag(); 1065 syncer::SyncError error; 1066 1067 error = model_associator_->DisassociateModels(); 1068 ASSERT_FALSE(error.IsSet()); 1069 { 1070 // Delete the first sync tab node. 1071 std::string tab_tag = TabNodePool::TabIdToTag(local_tag, 1); 1072 1073 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 1074 syncer::ReadNode root(&trans); 1075 root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS)); 1076 syncer::WriteNode tab_node(&trans); 1077 ASSERT_EQ(syncer::BaseNode::INIT_OK, 1078 tab_node.InitByClientTagLookup(syncer::SESSIONS, tab_tag)); 1079 tab_node.Tombstone(); 1080 } 1081 error = model_associator_->AssociateModels(NULL, NULL); 1082 ASSERT_FALSE(error.IsSet()); 1083 1084 // Add some more tabs to ensure we don't conflict with the pre-existing tab 1085 // node. 1086 AddTab(browser(), GURL("http://baz1")); 1087 AddTab(browser(), GURL("http://baz2")); 1088 } 1089 1090 TEST_F(ProfileSyncServiceSessionTest, Favicons) { 1091 CreateRootHelper create_root(this); 1092 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 1093 ASSERT_TRUE(create_root.success()); 1094 1095 // Build a foreign session with one window and one tab. 1096 std::string tag = "tag1"; 1097 sync_pb::SessionSpecifics meta; 1098 helper_.BuildSessionSpecifics(tag, &meta); 1099 std::vector<SessionID::id_type> tab_list; 1100 tab_list.push_back(5); 1101 helper_.AddWindowSpecifics(0, tab_list, &meta); 1102 sync_pb::SessionSpecifics tab; 1103 helper_.BuildTabSpecifics(tag, 0, tab_list[0], &tab); 1104 std::string url = tab.tab().navigation(0).virtual_url(); 1105 scoped_refptr<base::RefCountedMemory> favicon; 1106 1107 // Update associator. 1108 model_associator_->AssociateForeignSpecifics(meta, base::Time()); 1109 model_associator_->AssociateForeignSpecifics(tab, base::Time()); 1110 base::RunLoop().RunUntilIdle(); 1111 ASSERT_FALSE(model_associator_->GetSyncedFaviconForPageURL(url, &favicon)); 1112 1113 // Now add a favicon. 1114 tab.mutable_tab()->set_favicon_source("http://favicon_source.com/png.ico"); 1115 tab.mutable_tab()->set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON); 1116 tab.mutable_tab()->set_favicon("data"); 1117 model_associator_->AssociateForeignSpecifics(tab, base::Time()); 1118 base::RunLoop().RunUntilIdle(); 1119 ASSERT_TRUE(model_associator_->GetSyncedFaviconForPageURL(url, &favicon)); 1120 ASSERT_TRUE(CompareMemoryToString("data", favicon)); 1121 1122 // Simulate navigating away. The associator should not delete the favicon. 1123 tab.mutable_tab()->clear_navigation(); 1124 tab.mutable_tab()->add_navigation()->set_virtual_url("http://new_url.com"); 1125 tab.mutable_tab()->clear_favicon_source(); 1126 tab.mutable_tab()->clear_favicon_type(); 1127 tab.mutable_tab()->clear_favicon(); 1128 model_associator_->AssociateForeignSpecifics(tab, base::Time()); 1129 base::RunLoop().RunUntilIdle(); 1130 ASSERT_TRUE(model_associator_->GetSyncedFaviconForPageURL(url, &favicon)); 1131 } 1132 1133 TEST_F(ProfileSyncServiceSessionTest, CorruptedLocalHeader) { 1134 AddTab(browser(), GURL("http://foo1")); 1135 NavigateAndCommitActiveTab(GURL("http://foo2")); 1136 AddTab(browser(), GURL("http://bar1")); 1137 NavigateAndCommitActiveTab(GURL("http://bar2")); 1138 CreateRootHelper create_root(this); 1139 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 1140 std::string local_tag = model_associator_->GetCurrentMachineTag(); 1141 syncer::SyncError error; 1142 1143 error = model_associator_->DisassociateModels(); 1144 ASSERT_FALSE(error.IsSet()); 1145 { 1146 // Load the header node and clear it. 1147 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 1148 syncer::WriteNode header(&trans); 1149 ASSERT_EQ(syncer::BaseNode::INIT_OK, 1150 header.InitByClientTagLookup(syncer::SESSIONS, local_tag)); 1151 sync_pb::SessionSpecifics specifics; 1152 header.SetSessionSpecifics(specifics); 1153 } 1154 // Ensure we associate properly despite the pre-existing node with our local 1155 // tag. 1156 error = model_associator_->AssociateModels(NULL, NULL); 1157 ASSERT_FALSE(error.IsSet()); 1158 } 1159 1160 TEST_F(ProfileSyncServiceSessionTest, CheckPrerenderedWebContentsSwap) { 1161 AddTab(browser(), GURL("http://foo1")); 1162 NavigateAndCommitActiveTab(GURL("http://foo2")); 1163 CreateRootHelper create_root(this); 1164 // Test setup. 1165 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 1166 1167 syncer::SyncError error; 1168 // Initial association. 1169 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error)); 1170 ASSERT_FALSE(error.IsSet()); 1171 1172 // To simulate WebContents swap during prerendering, create new WebContents 1173 // and swap with old WebContents. 1174 content::WebContents* old_web_contents = 1175 browser()->tab_strip_model()->GetActiveWebContents(); 1176 1177 // Create new WebContents, with the required tab helpers. 1178 WebContents* new_web_contents = WebContents::CreateWithSessionStorage( 1179 WebContents::CreateParams(profile()), 1180 old_web_contents->GetController().GetSessionStorageNamespaceMap()); 1181 SessionTabHelper::CreateForWebContents(new_web_contents); 1182 TabContentsSyncedTabDelegate::CreateForWebContents(new_web_contents); 1183 new_web_contents->GetController() 1184 .CopyStateFrom(old_web_contents->GetController()); 1185 1186 // Swap the WebContents. 1187 int index = 1188 browser()->tab_strip_model()->GetIndexOfWebContents(old_web_contents); 1189 browser()->tab_strip_model()->ReplaceWebContentsAt(index, new_web_contents); 1190 1191 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error)); 1192 ASSERT_FALSE(error.IsSet()); 1193 // Navigate away. 1194 NavigateAndCommitActiveTab(GURL("http://bar2")); 1195 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error)); 1196 ASSERT_FALSE(error.IsSet()); 1197 1198 // Delete old WebContents. This should not crash. 1199 delete old_web_contents; 1200 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error)); 1201 ASSERT_FALSE(error.IsSet()); 1202 1203 // Try more navigations to make sure everything if fine. 1204 NavigateAndCommitActiveTab(GURL("http://bar3")); 1205 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error)); 1206 ASSERT_FALSE(error.IsSet()); 1207 1208 AddTab(browser(), GURL("http://bar4")); 1209 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error)); 1210 ASSERT_FALSE(error.IsSet()); 1211 NavigateAndCommitActiveTab(GURL("http://bar5")); 1212 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error)); 1213 ASSERT_FALSE(error.IsSet()); 1214 } 1215 1216 TEST_F(ProfileSyncServiceSessionTest, TabPoolFreeNodeLimits) { 1217 CreateRootHelper create_root(this); 1218 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 1219 ASSERT_TRUE(create_root.success()); 1220 1221 // Allocate TabNodePool::kFreeNodesHighWatermark + 1 nodes and verify that 1222 // freeing the last node reduces the free node pool size to 1223 // kFreeNodesLowWatermark. 1224 1225 SessionID session_id; 1226 std::vector<int> used_sync_ids; 1227 for (size_t i = 1; i <= TabNodePool::kFreeNodesHighWatermark + 1; ++i) { 1228 session_id.set_id(i); 1229 int sync_id = model_associator_->local_tab_pool_.GetFreeTabNode(); 1230 model_associator_->local_tab_pool_.AssociateTabNode(sync_id, i); 1231 used_sync_ids.push_back(sync_id); 1232 } 1233 1234 // Free all except one node. 1235 int last_sync_id = used_sync_ids.back(); 1236 used_sync_ids.pop_back(); 1237 1238 for (size_t i = 0; i < used_sync_ids.size(); ++i) { 1239 model_associator_->local_tab_pool_.FreeTabNode(used_sync_ids[i]); 1240 } 1241 1242 // Except one node all nodes should be in FreeNode pool. 1243 EXPECT_FALSE(model_associator_->local_tab_pool_.Full()); 1244 EXPECT_FALSE(model_associator_->local_tab_pool_.Empty()); 1245 // Total capacity = 1 Associated Node + kFreeNodesHighWatermark free node. 1246 EXPECT_EQ(TabNodePool::kFreeNodesHighWatermark + 1, 1247 model_associator_->local_tab_pool_.Capacity()); 1248 1249 // Freeing the last sync node should drop the free nodes to 1250 // kFreeNodesLowWatermark. 1251 model_associator_->local_tab_pool_.FreeTabNode(last_sync_id); 1252 EXPECT_FALSE(model_associator_->local_tab_pool_.Empty()); 1253 EXPECT_TRUE(model_associator_->local_tab_pool_.Full()); 1254 EXPECT_EQ(TabNodePool::kFreeNodesLowWatermark, 1255 model_associator_->local_tab_pool_.Capacity()); 1256 } 1257 1258 TEST_F(ProfileSyncServiceSessionTest, TabNodePoolDeleteUnassociatedNodes) { 1259 CreateRootHelper create_root(this); 1260 ASSERT_TRUE(StartSyncService(create_root.callback(), false)); 1261 std::string local_tag = model_associator_->GetCurrentMachineTag(); 1262 syncer::SyncError error; 1263 // Create a free node and then dissassociate sessions so that it ends up 1264 // unassociated. 1265 int tab_node_id = model_associator_->local_tab_pool_.GetFreeTabNode(); 1266 // Update the tab_id of the node, so that it is considered a valid 1267 // unassociated node otherwise it will be mistaken for a corrupted node and 1268 // will be deleted before being added to the tab node pool. 1269 { 1270 std::string tab_tag = TabNodePool::TabIdToTag(local_tag, tab_node_id); 1271 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 1272 syncer::WriteNode tab_node(&trans); 1273 ASSERT_EQ(syncer::BaseNode::INIT_OK, 1274 tab_node.InitByClientTagLookup(syncer::SESSIONS, tab_tag)); 1275 sync_pb::SessionSpecifics specifics = tab_node.GetSessionSpecifics(); 1276 sync_pb::SessionTab* tab = specifics.mutable_tab(); 1277 tab->set_tab_id(1); 1278 tab_node.SetSessionSpecifics(specifics); 1279 } 1280 1281 error = model_associator_->DisassociateModels(); 1282 ASSERT_FALSE(error.IsSet()); 1283 error = model_associator_->AssociateModels(NULL, NULL); 1284 ASSERT_FALSE(error.IsSet()); 1285 } 1286 1287 } // namespace browser_sync 1288