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