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