1 // Copyright 2014 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 "chrome/browser/sync/sessions/sessions_sync_manager.h" 6 7 #include "base/strings/string_util.h" 8 #include "chrome/browser/chrome_notification_types.h" 9 #include "chrome/browser/sessions/session_tab_helper.h" 10 #include "chrome/browser/sessions/session_types.h" 11 #include "chrome/browser/sync/glue/local_device_info_provider_mock.h" 12 #include "chrome/browser/sync/glue/session_sync_test_helper.h" 13 #include "chrome/browser/sync/glue/synced_tab_delegate.h" 14 #include "chrome/browser/sync/glue/synced_window_delegate.h" 15 #include "chrome/browser/sync/sessions/notification_service_sessions_router.h" 16 #include "chrome/browser/sync/sessions/sessions_util.h" 17 #include "chrome/browser/sync/sessions/synced_window_delegates_getter.h" 18 #include "chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h" 19 #include "chrome/browser/ui/tabs/tab_strip_model.h" 20 #include "chrome/test/base/browser_with_test_window_test.h" 21 #include "components/sessions/serialized_navigation_entry_test_helper.h" 22 #include "components/sessions/session_id.h" 23 #include "components/sync_driver/device_info.h" 24 #include "content/public/browser/navigation_entry.h" 25 #include "content/public/browser/notification_details.h" 26 #include "content/public/browser/notification_service.h" 27 #include "content/public/browser/notification_source.h" 28 #include "content/public/browser/web_contents.h" 29 #include "sync/api/attachments/attachment_id.h" 30 #include "sync/api/sync_error_factory_mock.h" 31 #include "sync/internal_api/public/attachments/attachment_service_proxy_for_test.h" 32 #include "testing/gmock/include/gmock/gmock.h" 33 #include "testing/gtest/include/gtest/gtest.h" 34 35 using content::WebContents; 36 using sessions::SerializedNavigationEntry; 37 using sessions::SerializedNavigationEntryTestHelper; 38 using sync_driver::DeviceInfo; 39 using sync_driver::LocalDeviceInfoProvider; 40 using syncer::SyncChange; 41 using syncer::SyncData; 42 43 namespace browser_sync { 44 45 namespace { 46 47 class SyncedWindowDelegateOverride : public SyncedWindowDelegate { 48 public: 49 explicit SyncedWindowDelegateOverride(SyncedWindowDelegate* wrapped) 50 : wrapped_(wrapped) { 51 } 52 virtual ~SyncedWindowDelegateOverride() {} 53 54 virtual bool HasWindow() const OVERRIDE { 55 return wrapped_->HasWindow(); 56 } 57 58 virtual SessionID::id_type GetSessionId() const OVERRIDE { 59 return wrapped_->GetSessionId(); 60 } 61 62 virtual int GetTabCount() const OVERRIDE { 63 return wrapped_->GetTabCount(); 64 } 65 66 virtual int GetActiveIndex() const OVERRIDE { 67 return wrapped_->GetActiveIndex(); 68 } 69 70 virtual bool IsApp() const OVERRIDE { 71 return wrapped_->IsApp(); 72 } 73 74 virtual bool IsTypeTabbed() const OVERRIDE { 75 return wrapped_->IsTypeTabbed(); 76 } 77 78 virtual bool IsTypePopup() const OVERRIDE { 79 return wrapped_->IsTypePopup(); 80 } 81 82 virtual bool IsTabPinned(const SyncedTabDelegate* tab) const OVERRIDE { 83 return wrapped_->IsTabPinned(tab); 84 } 85 86 virtual SyncedTabDelegate* GetTabAt(int index) const OVERRIDE { 87 if (tab_overrides_.find(index) != tab_overrides_.end()) 88 return tab_overrides_.find(index)->second; 89 90 return wrapped_->GetTabAt(index); 91 } 92 93 void OverrideTabAt(int index, 94 SyncedTabDelegate* delegate, 95 SessionID::id_type tab_id) { 96 tab_overrides_[index] = delegate; 97 tab_id_overrides_[index] = tab_id; 98 } 99 100 virtual SessionID::id_type GetTabIdAt(int index) const OVERRIDE { 101 if (tab_id_overrides_.find(index) != tab_id_overrides_.end()) 102 return tab_id_overrides_.find(index)->second; 103 return wrapped_->GetTabIdAt(index); 104 } 105 106 virtual bool IsSessionRestoreInProgress() const OVERRIDE { 107 return wrapped_->IsSessionRestoreInProgress(); 108 } 109 110 private: 111 std::map<int, SyncedTabDelegate*> tab_overrides_; 112 std::map<int, SessionID::id_type> tab_id_overrides_; 113 SyncedWindowDelegate* wrapped_; 114 }; 115 116 class TestSyncedWindowDelegatesGetter : public SyncedWindowDelegatesGetter { 117 public: 118 TestSyncedWindowDelegatesGetter( 119 const std::set<SyncedWindowDelegate*>& delegates) 120 : delegates_(delegates) {} 121 122 virtual const std::set<SyncedWindowDelegate*> GetSyncedWindowDelegates() 123 OVERRIDE { 124 return delegates_; 125 } 126 private: 127 const std::set<SyncedWindowDelegate*> delegates_; 128 }; 129 130 class TestSyncProcessorStub : public syncer::SyncChangeProcessor { 131 public: 132 explicit TestSyncProcessorStub(syncer::SyncChangeList* output) 133 : output_(output) {} 134 virtual syncer::SyncError ProcessSyncChanges( 135 const tracked_objects::Location& from_here, 136 const syncer::SyncChangeList& change_list) OVERRIDE { 137 if (error_.IsSet()) { 138 syncer::SyncError error = error_; 139 error_ = syncer::SyncError(); 140 return error; 141 } 142 143 if (output_) 144 output_->insert(output_->end(), change_list.begin(), change_list.end()); 145 146 return syncer::SyncError(); 147 } 148 149 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) 150 const OVERRIDE { 151 return sync_data_to_return_; 152 } 153 154 void FailProcessSyncChangesWith(const syncer::SyncError& error) { 155 error_ = error; 156 } 157 158 void SetSyncDataToReturn(const syncer::SyncDataList& data) { 159 sync_data_to_return_ = data; 160 } 161 162 private: 163 syncer::SyncError error_; 164 syncer::SyncChangeList* output_; 165 syncer::SyncDataList sync_data_to_return_; 166 }; 167 168 syncer::SyncChange MakeRemoteChange( 169 int64 id, 170 const sync_pb::SessionSpecifics& specifics, 171 SyncChange::SyncChangeType type) { 172 sync_pb::EntitySpecifics entity; 173 entity.mutable_session()->CopyFrom(specifics); 174 return syncer::SyncChange( 175 FROM_HERE, 176 type, 177 syncer::SyncData::CreateRemoteData( 178 id, 179 entity, 180 base::Time(), 181 syncer::AttachmentIdList(), 182 syncer::AttachmentServiceProxyForTest::Create())); 183 } 184 185 void AddTabsToChangeList( 186 const std::vector<sync_pb::SessionSpecifics>& batch, 187 SyncChange::SyncChangeType type, 188 syncer::SyncChangeList* change_list) { 189 std::vector<sync_pb::SessionSpecifics>::const_iterator iter; 190 for (iter = batch.begin(); 191 iter != batch.end(); ++iter) { 192 sync_pb::EntitySpecifics entity; 193 entity.mutable_session()->CopyFrom(*iter); 194 change_list->push_back(syncer::SyncChange( 195 FROM_HERE, 196 type, 197 syncer::SyncData::CreateRemoteData( 198 iter->tab_node_id(), 199 entity, 200 base::Time(), 201 syncer::AttachmentIdList(), 202 syncer::AttachmentServiceProxyForTest::Create()))); 203 } 204 } 205 206 void AddTabsToSyncDataList(const std::vector<sync_pb::SessionSpecifics> tabs, 207 syncer::SyncDataList* list) { 208 for (size_t i = 0; i < tabs.size(); i++) { 209 sync_pb::EntitySpecifics entity; 210 entity.mutable_session()->CopyFrom(tabs[i]); 211 list->push_back(SyncData::CreateRemoteData( 212 i + 2, 213 entity, 214 base::Time(), 215 syncer::AttachmentIdList(), 216 syncer::AttachmentServiceProxyForTest::Create())); 217 } 218 } 219 220 class DummyRouter : public LocalSessionEventRouter { 221 public: 222 virtual ~DummyRouter() {} 223 virtual void StartRoutingTo(LocalSessionEventHandler* handler) OVERRIDE {} 224 virtual void Stop() OVERRIDE {} 225 }; 226 227 scoped_ptr<LocalSessionEventRouter> NewDummyRouter() { 228 return scoped_ptr<LocalSessionEventRouter>(new DummyRouter()); 229 } 230 231 } // namespace 232 233 class SessionsSyncManagerTest 234 : public BrowserWithTestWindowTest { 235 public: 236 SessionsSyncManagerTest() 237 : test_processor_(NULL) { 238 local_device_.reset(new LocalDeviceInfoProviderMock( 239 "cache_guid", 240 "Wayne Gretzky's Hacking Box", 241 "Chromium 10k", 242 "Chrome 10k", 243 sync_pb::SyncEnums_DeviceType_TYPE_LINUX, 244 "device_id")); 245 } 246 247 virtual void SetUp() OVERRIDE { 248 BrowserWithTestWindowTest::SetUp(); 249 browser_sync::NotificationServiceSessionsRouter* router( 250 new browser_sync::NotificationServiceSessionsRouter( 251 profile(), syncer::SyncableService::StartSyncFlare())); 252 manager_.reset(new SessionsSyncManager(profile(), local_device_.get(), 253 scoped_ptr<LocalSessionEventRouter>(router))); 254 } 255 256 virtual void TearDown() OVERRIDE { 257 test_processor_ = NULL; 258 helper()->Reset(); 259 manager_.reset(); 260 BrowserWithTestWindowTest::TearDown(); 261 } 262 263 const DeviceInfo* GetLocalDeviceInfo() { 264 return local_device_->GetLocalDeviceInfo(); 265 } 266 267 SessionsSyncManager* manager() { return manager_.get(); } 268 SessionSyncTestHelper* helper() { return &helper_; } 269 LocalDeviceInfoProvider* local_device() { return local_device_.get(); } 270 271 void InitWithSyncDataTakeOutput(const syncer::SyncDataList& initial_data, 272 syncer::SyncChangeList* output) { 273 test_processor_ = new TestSyncProcessorStub(output); 274 syncer::SyncMergeResult result = manager_->MergeDataAndStartSyncing( 275 syncer::SESSIONS, initial_data, 276 scoped_ptr<syncer::SyncChangeProcessor>(test_processor_), 277 scoped_ptr<syncer::SyncErrorFactory>( 278 new syncer::SyncErrorFactoryMock())); 279 EXPECT_FALSE(result.error().IsSet()); 280 } 281 282 void InitWithNoSyncData() { 283 InitWithSyncDataTakeOutput(syncer::SyncDataList(), NULL); 284 } 285 286 void TriggerProcessSyncChangesError() { 287 test_processor_->FailProcessSyncChangesWith(syncer::SyncError( 288 FROM_HERE, syncer::SyncError::DATATYPE_ERROR, "Error", 289 syncer::SESSIONS)); 290 } 291 292 void SetSyncData(const syncer::SyncDataList& data) { 293 test_processor_->SetSyncDataToReturn(data); 294 } 295 296 syncer::SyncChangeList* FilterOutLocalHeaderChanges( 297 syncer::SyncChangeList* list) { 298 syncer::SyncChangeList::iterator it = list->begin(); 299 bool found = false; 300 while (it != list->end()) { 301 if (syncer::SyncDataLocal(it->sync_data()).GetTag() == 302 manager_->current_machine_tag()) { 303 EXPECT_TRUE(SyncChange::ACTION_ADD == it->change_type() || 304 SyncChange::ACTION_UPDATE == it->change_type()); 305 it = list->erase(it); 306 found = true; 307 } else { 308 ++it; 309 } 310 } 311 EXPECT_TRUE(found); 312 return list; 313 } 314 315 private: 316 scoped_ptr<SessionsSyncManager> manager_; 317 SessionSyncTestHelper helper_; 318 TestSyncProcessorStub* test_processor_; 319 scoped_ptr<LocalDeviceInfoProviderMock> local_device_; 320 }; 321 322 // Test that the SyncSessionManager can properly fill in a SessionHeader. 323 TEST_F(SessionsSyncManagerTest, PopulateSessionHeader) { 324 sync_pb::SessionHeader header_s; 325 header_s.set_client_name("Client 1"); 326 header_s.set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_WIN); 327 328 SyncedSession session; 329 base::Time time = base::Time::Now(); 330 SessionsSyncManager::PopulateSessionHeaderFromSpecifics( 331 header_s, time, &session); 332 ASSERT_EQ("Client 1", session.session_name); 333 ASSERT_EQ(SyncedSession::TYPE_WIN, session.device_type); 334 ASSERT_EQ(time, session.modified_time); 335 } 336 337 // Test translation between protobuf types and chrome session types. 338 TEST_F(SessionsSyncManagerTest, PopulateSessionWindow) { 339 sync_pb::SessionWindow window_s; 340 window_s.add_tab(0); 341 window_s.set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED); 342 window_s.set_selected_tab_index(1); 343 344 std::string tag = "tag"; 345 SyncedSession* session = manager()->session_tracker_.GetSession(tag); 346 manager()->session_tracker_.PutWindowInSession(tag, 0); 347 manager()->BuildSyncedSessionFromSpecifics( 348 tag, window_s, base::Time(), session->windows[0]); 349 ASSERT_EQ(1U, session->windows[0]->tabs.size()); 350 ASSERT_EQ(1, session->windows[0]->selected_tab_index); 351 ASSERT_EQ(1, session->windows[0]->type); 352 ASSERT_EQ(1U, manager()->session_tracker_.num_synced_sessions()); 353 ASSERT_EQ(1U, 354 manager()->session_tracker_.num_synced_tabs(std::string("tag"))); 355 } 356 357 namespace { 358 359 class SyncedTabDelegateFake : public SyncedTabDelegate { 360 public: 361 SyncedTabDelegateFake() : current_entry_index_(0), 362 pending_entry_index_(-1), 363 is_supervised_(false), 364 sync_id_(-1), 365 blocked_navigations_(NULL) {} 366 virtual ~SyncedTabDelegateFake() {} 367 368 virtual int GetCurrentEntryIndex() const OVERRIDE { 369 return current_entry_index_; 370 } 371 void set_current_entry_index(int i) { 372 current_entry_index_ = i; 373 } 374 375 virtual content::NavigationEntry* GetEntryAtIndex(int i) const OVERRIDE { 376 const int size = entries_.size(); 377 return (size < i + 1) ? NULL : entries_[i]; 378 } 379 380 void AppendEntry(content::NavigationEntry* entry) { 381 entries_.push_back(entry); 382 } 383 384 virtual int GetEntryCount() const OVERRIDE { 385 return entries_.size(); 386 } 387 388 virtual int GetPendingEntryIndex() const OVERRIDE { 389 return pending_entry_index_; 390 } 391 void set_pending_entry_index(int i) { 392 pending_entry_index_ = i; 393 } 394 395 virtual SessionID::id_type GetWindowId() const OVERRIDE { 396 return SessionID::id_type(); 397 } 398 399 virtual SessionID::id_type GetSessionId() const OVERRIDE { 400 return SessionID::id_type(); 401 } 402 403 virtual bool IsBeingDestroyed() const OVERRIDE { return false; } 404 virtual Profile* profile() const OVERRIDE { return NULL; } 405 virtual std::string GetExtensionAppId() const OVERRIDE { 406 return std::string(); 407 } 408 virtual content::NavigationEntry* GetPendingEntry() const OVERRIDE { 409 return NULL; 410 } 411 virtual content::NavigationEntry* GetActiveEntry() const OVERRIDE { 412 return NULL; 413 } 414 virtual bool ProfileIsSupervised() const OVERRIDE { 415 return is_supervised_; 416 } 417 void set_is_supervised(bool is_supervised) { is_supervised_ = is_supervised; } 418 virtual const std::vector<const content::NavigationEntry*>* 419 GetBlockedNavigations() const OVERRIDE { 420 return blocked_navigations_; 421 } 422 void set_blocked_navigations( 423 std::vector<const content::NavigationEntry*>* navs) { 424 blocked_navigations_ = navs; 425 } 426 virtual bool IsPinned() const OVERRIDE { 427 return false; 428 } 429 virtual bool HasWebContents() const OVERRIDE { 430 return false; 431 } 432 virtual content::WebContents* GetWebContents() const OVERRIDE { 433 return NULL; 434 } 435 436 // Session sync related methods. 437 virtual int GetSyncId() const OVERRIDE { 438 return sync_id_; 439 } 440 virtual void SetSyncId(int sync_id) OVERRIDE { 441 sync_id_ = sync_id; 442 } 443 444 void reset() { 445 current_entry_index_ = 0; 446 pending_entry_index_ = -1; 447 sync_id_ = -1; 448 entries_.clear(); 449 } 450 451 private: 452 int current_entry_index_; 453 int pending_entry_index_; 454 bool is_supervised_; 455 int sync_id_; 456 std::vector<const content::NavigationEntry*>* blocked_navigations_; 457 ScopedVector<content::NavigationEntry> entries_; 458 }; 459 460 } // namespace 461 462 // Test that we exclude tabs with only chrome:// and file:// schemed navigations 463 // from ShouldSyncTab(..). 464 TEST_F(SessionsSyncManagerTest, ValidTabs) { 465 SyncedTabDelegateFake tab; 466 467 // A null entry shouldn't crash. 468 tab.AppendEntry(NULL); 469 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab)); 470 tab.reset(); 471 472 // A chrome:// entry isn't valid. 473 content::NavigationEntry* entry(content::NavigationEntry::Create()); 474 entry->SetVirtualURL(GURL("chrome://preferences/")); 475 tab.AppendEntry(entry); 476 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab)); 477 478 479 // A file:// entry isn't valid, even in addition to another entry. 480 content::NavigationEntry* entry2(content::NavigationEntry::Create()); 481 entry2->SetVirtualURL(GURL("file://bla")); 482 tab.AppendEntry(entry2); 483 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab)); 484 485 // Add a valid scheme entry to tab, making the tab valid. 486 content::NavigationEntry* entry3(content::NavigationEntry::Create()); 487 entry3->SetVirtualURL(GURL("http://www.google.com")); 488 tab.AppendEntry(entry3); 489 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab)); 490 } 491 492 // Make sure GetCurrentVirtualURL() returns the virtual URL of the pending 493 // entry if the current entry is pending. 494 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLPending) { 495 SyncedTabDelegateFake tab; 496 content::NavigationEntry* entry(content::NavigationEntry::Create()); 497 entry->SetVirtualURL(GURL("http://www.google.com")); 498 tab.AppendEntry(entry); 499 EXPECT_EQ(entry->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab)); 500 } 501 502 // Make sure GetCurrentVirtualURL() returns the virtual URL of the current 503 // entry if the current entry is non-pending. 504 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLNonPending) { 505 SyncedTabDelegateFake tab; 506 content::NavigationEntry* entry(content::NavigationEntry::Create()); 507 entry->SetVirtualURL(GURL("http://www.google.com")); 508 tab.AppendEntry(entry); 509 EXPECT_EQ(entry->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab)); 510 } 511 512 static const base::Time kTime0 = base::Time::FromInternalValue(100); 513 static const base::Time kTime1 = base::Time::FromInternalValue(110); 514 static const base::Time kTime2 = base::Time::FromInternalValue(120); 515 static const base::Time kTime3 = base::Time::FromInternalValue(130); 516 static const base::Time kTime4 = base::Time::FromInternalValue(140); 517 static const base::Time kTime5 = base::Time::FromInternalValue(150); 518 static const base::Time kTime6 = base::Time::FromInternalValue(160); 519 static const base::Time kTime7 = base::Time::FromInternalValue(170); 520 static const base::Time kTime8 = base::Time::FromInternalValue(180); 521 static const base::Time kTime9 = base::Time::FromInternalValue(190); 522 523 // Populate the mock tab delegate with some data and navigation 524 // entries and make sure that setting a SessionTab from it preserves 525 // those entries (and clobbers any existing data). 526 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegate) { 527 // Create a tab with three valid entries. 528 SyncedTabDelegateFake tab; 529 content::NavigationEntry* entry1(content::NavigationEntry::Create()); 530 entry1->SetVirtualURL(GURL("http://www.google.com")); 531 entry1->SetTimestamp(kTime1); 532 entry1->SetHttpStatusCode(200); 533 content::NavigationEntry* entry2(content::NavigationEntry::Create()); 534 entry2->SetVirtualURL(GURL("http://www.noodle.com")); 535 entry2->SetTimestamp(kTime2); 536 entry2->SetHttpStatusCode(201); 537 content::NavigationEntry* entry3(content::NavigationEntry::Create()); 538 entry3->SetVirtualURL(GURL("http://www.doodle.com")); 539 entry3->SetTimestamp(kTime3); 540 entry3->SetHttpStatusCode(202); 541 542 tab.AppendEntry(entry1); 543 tab.AppendEntry(entry2); 544 tab.AppendEntry(entry3); 545 tab.set_current_entry_index(2); 546 547 SessionTab session_tab; 548 session_tab.window_id.set_id(1); 549 session_tab.tab_id.set_id(1); 550 session_tab.tab_visual_index = 1; 551 session_tab.current_navigation_index = 1; 552 session_tab.pinned = true; 553 session_tab.extension_app_id = "app id"; 554 session_tab.user_agent_override = "override"; 555 session_tab.timestamp = kTime5; 556 session_tab.navigations.push_back( 557 SerializedNavigationEntryTestHelper::CreateNavigation( 558 "http://www.example.com", "Example")); 559 session_tab.session_storage_persistent_id = "persistent id"; 560 manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab); 561 562 EXPECT_EQ(0, session_tab.window_id.id()); 563 EXPECT_EQ(0, session_tab.tab_id.id()); 564 EXPECT_EQ(0, session_tab.tab_visual_index); 565 EXPECT_EQ(2, session_tab.current_navigation_index); 566 EXPECT_FALSE(session_tab.pinned); 567 EXPECT_TRUE(session_tab.extension_app_id.empty()); 568 EXPECT_TRUE(session_tab.user_agent_override.empty()); 569 EXPECT_EQ(kTime4, session_tab.timestamp); 570 ASSERT_EQ(3u, session_tab.navigations.size()); 571 EXPECT_EQ(entry1->GetVirtualURL(), 572 session_tab.navigations[0].virtual_url()); 573 EXPECT_EQ(entry2->GetVirtualURL(), 574 session_tab.navigations[1].virtual_url()); 575 EXPECT_EQ(entry3->GetVirtualURL(), 576 session_tab.navigations[2].virtual_url()); 577 EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp()); 578 EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp()); 579 EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp()); 580 EXPECT_EQ(200, session_tab.navigations[0].http_status_code()); 581 EXPECT_EQ(201, session_tab.navigations[1].http_status_code()); 582 EXPECT_EQ(202, session_tab.navigations[2].http_status_code()); 583 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID, 584 session_tab.navigations[0].blocked_state()); 585 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID, 586 session_tab.navigations[1].blocked_state()); 587 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID, 588 session_tab.navigations[2].blocked_state()); 589 EXPECT_TRUE(session_tab.session_storage_persistent_id.empty()); 590 } 591 592 // Ensure the current_navigation_index gets set properly when the navigation 593 // stack gets trucated to +/- 6 entries. 594 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegateNavigationIndex) { 595 SyncedTabDelegateFake tab; 596 content::NavigationEntry* entry0(content::NavigationEntry::Create()); 597 entry0->SetVirtualURL(GURL("http://www.google.com")); 598 entry0->SetTimestamp(kTime0); 599 entry0->SetHttpStatusCode(200); 600 content::NavigationEntry* entry1(content::NavigationEntry::Create()); 601 entry1->SetVirtualURL(GURL("http://www.zoogle.com")); 602 entry1->SetTimestamp(kTime1); 603 entry1->SetHttpStatusCode(200); 604 content::NavigationEntry* entry2(content::NavigationEntry::Create()); 605 entry2->SetVirtualURL(GURL("http://www.noogle.com")); 606 entry2->SetTimestamp(kTime2); 607 entry2->SetHttpStatusCode(200); 608 content::NavigationEntry* entry3(content::NavigationEntry::Create()); 609 entry3->SetVirtualURL(GURL("http://www.doogle.com")); 610 entry3->SetTimestamp(kTime3); 611 entry3->SetHttpStatusCode(200); 612 content::NavigationEntry* entry4(content::NavigationEntry::Create()); 613 entry4->SetVirtualURL(GURL("http://www.yoogle.com")); 614 entry4->SetTimestamp(kTime4); 615 entry4->SetHttpStatusCode(200); 616 content::NavigationEntry* entry5(content::NavigationEntry::Create()); 617 entry5->SetVirtualURL(GURL("http://www.foogle.com")); 618 entry5->SetTimestamp(kTime5); 619 entry5->SetHttpStatusCode(200); 620 content::NavigationEntry* entry6(content::NavigationEntry::Create()); 621 entry6->SetVirtualURL(GURL("http://www.boogle.com")); 622 entry6->SetTimestamp(kTime6); 623 entry6->SetHttpStatusCode(200); 624 content::NavigationEntry* entry7(content::NavigationEntry::Create()); 625 entry7->SetVirtualURL(GURL("http://www.moogle.com")); 626 entry7->SetTimestamp(kTime7); 627 entry7->SetHttpStatusCode(200); 628 content::NavigationEntry* entry8(content::NavigationEntry::Create()); 629 entry8->SetVirtualURL(GURL("http://www.poogle.com")); 630 entry8->SetTimestamp(kTime8); 631 entry8->SetHttpStatusCode(200); 632 content::NavigationEntry* entry9(content::NavigationEntry::Create()); 633 entry9->SetVirtualURL(GURL("http://www.roogle.com")); 634 entry9->SetTimestamp(kTime9); 635 entry9->SetHttpStatusCode(200); 636 637 tab.AppendEntry(entry0); 638 tab.AppendEntry(entry1); 639 tab.AppendEntry(entry2); 640 tab.AppendEntry(entry3); 641 tab.AppendEntry(entry4); 642 tab.AppendEntry(entry5); 643 tab.AppendEntry(entry6); 644 tab.AppendEntry(entry7); 645 tab.AppendEntry(entry8); 646 tab.AppendEntry(entry9); 647 tab.set_current_entry_index(8); 648 649 SessionTab session_tab; 650 manager()->SetSessionTabFromDelegate(tab, kTime9, &session_tab); 651 652 EXPECT_EQ(6, session_tab.current_navigation_index); 653 ASSERT_EQ(8u, session_tab.navigations.size()); 654 EXPECT_EQ(entry2->GetVirtualURL(), 655 session_tab.navigations[0].virtual_url()); 656 EXPECT_EQ(entry3->GetVirtualURL(), 657 session_tab.navigations[1].virtual_url()); 658 EXPECT_EQ(entry4->GetVirtualURL(), 659 session_tab.navigations[2].virtual_url()); 660 } 661 662 // Ensure the current_navigation_index gets set to the end of the navigation 663 // stack if the current navigation is invalid. 664 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegateCurrentInvalid) { 665 SyncedTabDelegateFake tab; 666 content::NavigationEntry* entry0(content::NavigationEntry::Create()); 667 entry0->SetVirtualURL(GURL("http://www.google.com")); 668 entry0->SetTimestamp(kTime0); 669 entry0->SetHttpStatusCode(200); 670 content::NavigationEntry* entry1(content::NavigationEntry::Create()); 671 entry1->SetVirtualURL(GURL("")); 672 entry1->SetTimestamp(kTime1); 673 entry1->SetHttpStatusCode(200); 674 content::NavigationEntry* entry2(content::NavigationEntry::Create()); 675 entry2->SetVirtualURL(GURL("http://www.noogle.com")); 676 entry2->SetTimestamp(kTime2); 677 entry2->SetHttpStatusCode(200); 678 content::NavigationEntry* entry3(content::NavigationEntry::Create()); 679 entry3->SetVirtualURL(GURL("http://www.doogle.com")); 680 entry3->SetTimestamp(kTime3); 681 entry3->SetHttpStatusCode(200); 682 683 tab.AppendEntry(entry0); 684 tab.AppendEntry(entry1); 685 tab.AppendEntry(entry2); 686 tab.AppendEntry(entry3); 687 tab.set_current_entry_index(1); 688 689 SessionTab session_tab; 690 manager()->SetSessionTabFromDelegate(tab, kTime9, &session_tab); 691 692 EXPECT_EQ(2, session_tab.current_navigation_index); 693 ASSERT_EQ(3u, session_tab.navigations.size()); 694 } 695 696 // Tests that for supervised users blocked navigations are recorded and marked 697 // as such, while regular navigations are marked as allowed. 698 TEST_F(SessionsSyncManagerTest, BlockedNavigations) { 699 SyncedTabDelegateFake tab; 700 content::NavigationEntry* entry1(content::NavigationEntry::Create()); 701 entry1->SetVirtualURL(GURL("http://www.google.com")); 702 entry1->SetTimestamp(kTime1); 703 tab.AppendEntry(entry1); 704 705 content::NavigationEntry* entry2 = content::NavigationEntry::Create(); 706 entry2->SetVirtualURL(GURL("http://blocked.com/foo")); 707 entry2->SetTimestamp(kTime2); 708 content::NavigationEntry* entry3 = content::NavigationEntry::Create(); 709 entry3->SetVirtualURL(GURL("http://evil.com")); 710 entry3->SetTimestamp(kTime3); 711 ScopedVector<const content::NavigationEntry> blocked_navigations; 712 blocked_navigations.push_back(entry2); 713 blocked_navigations.push_back(entry3); 714 715 tab.set_is_supervised(true); 716 tab.set_blocked_navigations(&blocked_navigations.get()); 717 718 SessionTab session_tab; 719 session_tab.window_id.set_id(1); 720 session_tab.tab_id.set_id(1); 721 session_tab.tab_visual_index = 1; 722 session_tab.current_navigation_index = 1; 723 session_tab.pinned = true; 724 session_tab.extension_app_id = "app id"; 725 session_tab.user_agent_override = "override"; 726 session_tab.timestamp = kTime5; 727 session_tab.navigations.push_back( 728 SerializedNavigationEntryTestHelper::CreateNavigation( 729 "http://www.example.com", "Example")); 730 session_tab.session_storage_persistent_id = "persistent id"; 731 manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab); 732 733 EXPECT_EQ(0, session_tab.window_id.id()); 734 EXPECT_EQ(0, session_tab.tab_id.id()); 735 EXPECT_EQ(0, session_tab.tab_visual_index); 736 EXPECT_EQ(0, session_tab.current_navigation_index); 737 EXPECT_FALSE(session_tab.pinned); 738 EXPECT_TRUE(session_tab.extension_app_id.empty()); 739 EXPECT_TRUE(session_tab.user_agent_override.empty()); 740 EXPECT_EQ(kTime4, session_tab.timestamp); 741 ASSERT_EQ(3u, session_tab.navigations.size()); 742 EXPECT_EQ(entry1->GetVirtualURL(), 743 session_tab.navigations[0].virtual_url()); 744 EXPECT_EQ(entry2->GetVirtualURL(), 745 session_tab.navigations[1].virtual_url()); 746 EXPECT_EQ(entry3->GetVirtualURL(), 747 session_tab.navigations[2].virtual_url()); 748 EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp()); 749 EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp()); 750 EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp()); 751 EXPECT_EQ(SerializedNavigationEntry::STATE_ALLOWED, 752 session_tab.navigations[0].blocked_state()); 753 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED, 754 session_tab.navigations[1].blocked_state()); 755 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED, 756 session_tab.navigations[2].blocked_state()); 757 EXPECT_TRUE(session_tab.session_storage_persistent_id.empty()); 758 } 759 760 // Tests that the local session header objects is created properly in 761 // presence of no other session activity, once and only once. 762 TEST_F(SessionsSyncManagerTest, MergeLocalSessionNoTabs) { 763 syncer::SyncChangeList out; 764 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out); 765 EXPECT_FALSE(manager()->current_machine_tag().empty()); 766 767 EXPECT_EQ(2U, out.size()); 768 EXPECT_TRUE(out[0].IsValid()); 769 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type()); 770 const SyncData data(out[0].sync_data()); 771 EXPECT_EQ(manager()->current_machine_tag(), 772 syncer::SyncDataLocal(data).GetTag()); 773 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session()); 774 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag()); 775 EXPECT_TRUE(specifics.has_header()); 776 const sync_pb::SessionHeader& header_s = specifics.header(); 777 EXPECT_TRUE(header_s.has_device_type()); 778 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name()); 779 EXPECT_EQ(0, header_s.window_size()); 780 781 EXPECT_TRUE(out[1].IsValid()); 782 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type()); 783 const SyncData data_2(out[1].sync_data()); 784 EXPECT_EQ(manager()->current_machine_tag(), 785 syncer::SyncDataLocal(data_2).GetTag()); 786 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session()); 787 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag()); 788 EXPECT_TRUE(specifics2.has_header()); 789 const sync_pb::SessionHeader& header_s2 = specifics2.header(); 790 EXPECT_EQ(0, header_s2.window_size()); 791 792 // Now take that header node and feed it in as input. 793 SyncData d(SyncData::CreateRemoteData( 794 1, 795 data.GetSpecifics(), 796 base::Time(), 797 syncer::AttachmentIdList(), 798 syncer::AttachmentServiceProxyForTest::Create())); 799 syncer::SyncDataList in(&d, &d + 1); 800 out.clear(); 801 SessionsSyncManager manager2(profile(), local_device(), NewDummyRouter()); 802 syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing( 803 syncer::SESSIONS, in, 804 scoped_ptr<syncer::SyncChangeProcessor>( 805 new TestSyncProcessorStub(&out)), 806 scoped_ptr<syncer::SyncErrorFactory>( 807 new syncer::SyncErrorFactoryMock())); 808 ASSERT_FALSE(result.error().IsSet()); 809 810 EXPECT_EQ(1U, out.size()); 811 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type()); 812 EXPECT_TRUE(out[0].sync_data().GetSpecifics().session().has_header()); 813 } 814 815 // Ensure model association associates the pre-existing tabs. 816 TEST_F(SessionsSyncManagerTest, SwappedOutOnRestore) { 817 AddTab(browser(), GURL("http://foo1")); 818 NavigateAndCommitActiveTab(GURL("http://foo2")); 819 AddTab(browser(), GURL("http://bar1")); 820 NavigateAndCommitActiveTab(GURL("http://bar2")); 821 AddTab(browser(), GURL("http://baz1")); 822 NavigateAndCommitActiveTab(GURL("http://baz2")); 823 const int kRestoredTabId = 1337; 824 const int kNewTabId = 2468; 825 826 syncer::SyncDataList in; 827 syncer::SyncChangeList out; 828 InitWithSyncDataTakeOutput(in, &out); 829 830 // Should be one header add, 3 tab add/update pairs, one header update. 831 ASSERT_EQ(8U, out.size()); 832 833 // For input, we set up: 834 // * one "normal" fully loaded tab 835 // * one "frozen" tab with no WebContents and a tab_id change 836 // * one "frozen" tab with no WebContents and no tab_id change 837 SyncData t0(SyncData::CreateRemoteData( 838 1, 839 out[2].sync_data().GetSpecifics(), 840 base::Time(), 841 syncer::AttachmentIdList(), 842 syncer::AttachmentServiceProxyForTest::Create())); 843 sync_pb::EntitySpecifics entity(out[4].sync_data().GetSpecifics()); 844 entity.mutable_session()->mutable_tab()->set_tab_id(kRestoredTabId); 845 SyncData t1(SyncData::CreateRemoteData( 846 2, 847 entity, 848 base::Time(), 849 syncer::AttachmentIdList(), 850 syncer::AttachmentServiceProxyForTest::Create())); 851 SyncData t2(SyncData::CreateRemoteData( 852 3, 853 out[6].sync_data().GetSpecifics(), 854 base::Time(), 855 syncer::AttachmentIdList(), 856 syncer::AttachmentServiceProxyForTest::Create())); 857 in.push_back(t0); 858 in.push_back(t1); 859 in.push_back(t2); 860 out.clear(); 861 manager()->StopSyncing(syncer::SESSIONS); 862 863 const std::set<SyncedWindowDelegate*> windows( 864 SyncedWindowDelegate::GetSyncedWindowDelegates()); 865 ASSERT_EQ(1U, windows.size()); 866 SyncedTabDelegateFake t1_override, t2_override; 867 t1_override.SetSyncId(1); // No WebContents by default. 868 t2_override.SetSyncId(2); // No WebContents by default. 869 SyncedWindowDelegateOverride window_override(*windows.begin()); 870 window_override.OverrideTabAt(1, &t1_override, kNewTabId); 871 window_override.OverrideTabAt(2, &t2_override, 872 t2.GetSpecifics().session().tab().tab_id()); 873 std::set<SyncedWindowDelegate*> delegates; 874 delegates.insert(&window_override); 875 scoped_ptr<TestSyncedWindowDelegatesGetter> getter( 876 new TestSyncedWindowDelegatesGetter(delegates)); 877 manager()->synced_window_getter_.reset(getter.release()); 878 879 syncer::SyncMergeResult result = manager()->MergeDataAndStartSyncing( 880 syncer::SESSIONS, in, 881 scoped_ptr<syncer::SyncChangeProcessor>( 882 new TestSyncProcessorStub(&out)), 883 scoped_ptr<syncer::SyncErrorFactory>( 884 new syncer::SyncErrorFactoryMock())); 885 886 // There should be two changes, one for the fully associated tab, and 887 // one for the tab_id update to t1. t2 shouldn't need to be updated. 888 ASSERT_EQ(2U, FilterOutLocalHeaderChanges(&out)->size()); 889 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type()); 890 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type()); 891 EXPECT_EQ(kNewTabId, 892 out[1].sync_data().GetSpecifics().session().tab().tab_id()); 893 894 // Verify TabLinks. 895 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_; 896 ASSERT_EQ(3U, tab_map.size()); 897 int t2_tab_id = t2.GetSpecifics().session().tab().tab_id(); 898 EXPECT_EQ(2, tab_map.find(t2_tab_id)->second->tab_node_id()); 899 EXPECT_EQ(1, tab_map.find(kNewTabId)->second->tab_node_id()); 900 int t0_tab_id = out[0].sync_data().GetSpecifics().session().tab().tab_id(); 901 EXPECT_EQ(0, tab_map.find(t0_tab_id)->second->tab_node_id()); 902 // TODO(tim): Once bug 337057 is fixed, we can issue an OnLocalTabModified 903 // from here (using an override similar to above) to return a new tab id 904 // and verify that we don't see any node creations in the SyncChangeProcessor 905 // (similar to how SessionsSyncManagerTest.OnLocalTabModified works.) 906 } 907 908 // Tests MergeDataAndStartSyncing with sync data but no local data. 909 TEST_F(SessionsSyncManagerTest, MergeWithInitialForeignSession) { 910 std::string tag = "tag1"; 911 912 SessionID::id_type n1[] = {5, 10, 13, 17}; 913 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1)); 914 std::vector<sync_pb::SessionSpecifics> tabs1; 915 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession( 916 tag, tab_list1, &tabs1)); 917 // Add a second window. 918 SessionID::id_type n2[] = {7, 15, 18, 20}; 919 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2)); 920 helper()->AddWindowSpecifics(1, tab_list2, &meta); 921 922 // Set up initial data. 923 syncer::SyncDataList initial_data; 924 sync_pb::EntitySpecifics entity; 925 entity.mutable_session()->CopyFrom(meta); 926 initial_data.push_back(SyncData::CreateRemoteData( 927 1, 928 entity, 929 base::Time(), 930 syncer::AttachmentIdList(), 931 syncer::AttachmentServiceProxyForTest::Create())); 932 AddTabsToSyncDataList(tabs1, &initial_data); 933 934 for (size_t i = 0; i < tab_list2.size(); ++i) { 935 sync_pb::EntitySpecifics entity; 936 helper()->BuildTabSpecifics(tag, 0, tab_list2[i], 937 entity.mutable_session()); 938 initial_data.push_back(SyncData::CreateRemoteData( 939 i + 10, 940 entity, 941 base::Time(), 942 syncer::AttachmentIdList(), 943 syncer::AttachmentServiceProxyForTest::Create())); 944 } 945 946 syncer::SyncChangeList output; 947 InitWithSyncDataTakeOutput(initial_data, &output); 948 EXPECT_TRUE(FilterOutLocalHeaderChanges(&output)->empty()); 949 950 std::vector<const SyncedSession*> foreign_sessions; 951 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 952 ASSERT_EQ(1U, foreign_sessions.size()); 953 std::vector<std::vector<SessionID::id_type> > session_reference; 954 session_reference.push_back(tab_list1); 955 session_reference.push_back(tab_list2); 956 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 957 } 958 959 // This is a combination of MergeWithInitialForeignSession and 960 // MergeLocalSessionExistingTabs. We repeat some checks performed in each of 961 // those tests to ensure the common mixed scenario works. 962 TEST_F(SessionsSyncManagerTest, MergeWithLocalAndForeignTabs) { 963 // Local. 964 AddTab(browser(), GURL("http://foo1")); 965 NavigateAndCommitActiveTab(GURL("http://foo2")); 966 967 // Foreign. 968 std::string tag = "tag1"; 969 SessionID::id_type n1[] = {5, 10, 13, 17}; 970 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1)); 971 std::vector<sync_pb::SessionSpecifics> tabs1; 972 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession( 973 tag, tab_list1, &tabs1)); 974 syncer::SyncDataList foreign_data; 975 sync_pb::EntitySpecifics entity; 976 entity.mutable_session()->CopyFrom(meta); 977 foreign_data.push_back(SyncData::CreateRemoteData( 978 1, 979 entity, 980 base::Time(), 981 syncer::AttachmentIdList(), 982 syncer::AttachmentServiceProxyForTest::Create())); 983 AddTabsToSyncDataList(tabs1, &foreign_data); 984 985 syncer::SyncChangeList output; 986 InitWithSyncDataTakeOutput(foreign_data, &output); 987 ASSERT_EQ(4U, output.size()); 988 989 // Verify the local header. 990 EXPECT_TRUE(output[0].IsValid()); 991 EXPECT_EQ(SyncChange::ACTION_ADD, output[0].change_type()); 992 const SyncData data(output[0].sync_data()); 993 EXPECT_EQ(manager()->current_machine_tag(), 994 syncer::SyncDataLocal(data).GetTag()); 995 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session()); 996 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag()); 997 EXPECT_TRUE(specifics.has_header()); 998 const sync_pb::SessionHeader& header_s = specifics.header(); 999 EXPECT_TRUE(header_s.has_device_type()); 1000 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name()); 1001 EXPECT_EQ(0, header_s.window_size()); 1002 1003 // Verify the tab node creations and updates with content. 1004 for (int i = 1; i < 3; i++) { 1005 EXPECT_TRUE(output[i].IsValid()); 1006 const SyncData data(output[i].sync_data()); 1007 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(), 1008 manager()->current_machine_tag(), true)); 1009 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session()); 1010 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag()); 1011 } 1012 EXPECT_EQ(SyncChange::ACTION_ADD, output[1].change_type()); 1013 EXPECT_EQ(SyncChange::ACTION_UPDATE, output[2].change_type()); 1014 EXPECT_TRUE(output[2].sync_data().GetSpecifics().session().has_tab()); 1015 1016 // Verify the header was updated to reflect window state. 1017 EXPECT_TRUE(output[3].IsValid()); 1018 EXPECT_EQ(SyncChange::ACTION_UPDATE, output[3].change_type()); 1019 const SyncData data_2(output[3].sync_data()); 1020 EXPECT_EQ(manager()->current_machine_tag(), 1021 syncer::SyncDataLocal(data_2).GetTag()); 1022 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session()); 1023 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag()); 1024 EXPECT_TRUE(specifics2.has_header()); 1025 const sync_pb::SessionHeader& header_s2 = specifics2.header(); 1026 EXPECT_EQ(1, header_s2.window_size()); 1027 1028 // Verify foreign data. 1029 std::vector<const SyncedSession*> foreign_sessions; 1030 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1031 std::vector<std::vector<SessionID::id_type> > session_reference; 1032 session_reference.push_back(tab_list1); 1033 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 1034 // There should be one and only one foreign session. If VerifySyncedSession 1035 // was successful above this EXPECT call ensures the local session didn't 1036 // get mistakenly added to foreign tracking (Similar to ExistingTabs test). 1037 EXPECT_EQ(1U, foreign_sessions.size()); 1038 } 1039 1040 // Tests the common scenario. Merge with both local and foreign session data 1041 // followed by updates flowing from sync and local. 1042 TEST_F(SessionsSyncManagerTest, UpdatesAfterMixedMerge) { 1043 // Add local and foreign data. 1044 AddTab(browser(), GURL("http://foo1")); 1045 NavigateAndCommitActiveTab(GURL("http://foo2")); 1046 1047 std::string tag1 = "tag1"; 1048 syncer::SyncDataList foreign_data1; 1049 std::vector<std::vector<SessionID::id_type> > meta1_reference; 1050 sync_pb::SessionSpecifics meta1; 1051 1052 SessionID::id_type n1[] = {5, 10, 13, 17}; 1053 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1)); 1054 meta1_reference.push_back(tab_list1); 1055 std::vector<sync_pb::SessionSpecifics> tabs1; 1056 meta1 = helper()->BuildForeignSession(tag1, tab_list1, &tabs1); 1057 sync_pb::EntitySpecifics entity; 1058 entity.mutable_session()->CopyFrom(meta1); 1059 foreign_data1.push_back(SyncData::CreateRemoteData( 1060 1, 1061 entity, 1062 base::Time(), 1063 syncer::AttachmentIdList(), 1064 syncer::AttachmentServiceProxyForTest::Create())); 1065 AddTabsToSyncDataList(tabs1, &foreign_data1); 1066 1067 syncer::SyncChangeList output1; 1068 InitWithSyncDataTakeOutput(foreign_data1, &output1); 1069 ASSERT_EQ(4U, output1.size()); 1070 1071 // Add a second window to the foreign session. 1072 // TODO(tim): Bug 98892. Add local window too when observers are hooked up. 1073 SessionID::id_type tab_nums2[] = {7, 15, 18, 20}; 1074 std::vector<SessionID::id_type> tab_list2( 1075 tab_nums2, tab_nums2 + arraysize(tab_nums2)); 1076 meta1_reference.push_back(tab_list2); 1077 helper()->AddWindowSpecifics(1, tab_list2, &meta1); 1078 std::vector<sync_pb::SessionSpecifics> tabs2; 1079 tabs2.resize(tab_list2.size()); 1080 for (size_t i = 0; i < tab_list2.size(); ++i) { 1081 helper()->BuildTabSpecifics(tag1, 0, tab_list2[i], &tabs2[i]); 1082 } 1083 1084 syncer::SyncChangeList changes; 1085 changes.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE)); 1086 AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes); 1087 manager()->ProcessSyncChanges(FROM_HERE, changes); 1088 changes.clear(); 1089 1090 // Check that the foreign session was associated and retrieve the data. 1091 std::vector<const SyncedSession*> foreign_sessions; 1092 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1093 ASSERT_EQ(1U, foreign_sessions.size()); 1094 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size()); 1095 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size()); 1096 helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0])); 1097 1098 // Add a new foreign session. 1099 std::string tag2 = "tag2"; 1100 SessionID::id_type n2[] = {107, 115}; 1101 std::vector<SessionID::id_type> tag2_tab_list(n2, n2 + arraysize(n2)); 1102 std::vector<sync_pb::SessionSpecifics> tag2_tabs; 1103 sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession( 1104 tag2, tag2_tab_list, &tag2_tabs)); 1105 changes.push_back(MakeRemoteChange(100, meta2, SyncChange::ACTION_ADD)); 1106 AddTabsToChangeList(tag2_tabs, SyncChange::ACTION_ADD, &changes); 1107 1108 manager()->ProcessSyncChanges(FROM_HERE, changes); 1109 changes.clear(); 1110 1111 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1112 std::vector<std::vector<SessionID::id_type> > meta2_reference; 1113 meta2_reference.push_back(tag2_tab_list); 1114 ASSERT_EQ(2U, foreign_sessions.size()); 1115 ASSERT_EQ(2U, foreign_sessions[1]->windows.find(0)->second->tabs.size()); 1116 helper()->VerifySyncedSession(tag2, meta2_reference, *(foreign_sessions[1])); 1117 foreign_sessions.clear(); 1118 1119 // Remove a tab from a window. 1120 meta1_reference[0].pop_back(); 1121 tab_list1.pop_back(); 1122 sync_pb::SessionWindow* win = meta1.mutable_header()->mutable_window(0); 1123 win->clear_tab(); 1124 for (std::vector<int>::const_iterator iter = tab_list1.begin(); 1125 iter != tab_list1.end(); ++iter) { 1126 win->add_tab(*iter); 1127 } 1128 syncer::SyncChangeList removal; 1129 removal.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE)); 1130 AddTabsToChangeList(tabs1, SyncChange::ACTION_UPDATE, &removal); 1131 manager()->ProcessSyncChanges(FROM_HERE, removal); 1132 1133 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1134 ASSERT_EQ(2U, foreign_sessions.size()); 1135 ASSERT_EQ(3U, foreign_sessions[0]->windows.find(0)->second->tabs.size()); 1136 helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0])); 1137 } 1138 1139 // Tests that this SyncSessionManager knows how to delete foreign sessions 1140 // if it wants to. 1141 TEST_F(SessionsSyncManagerTest, DeleteForeignSession) { 1142 InitWithNoSyncData(); 1143 std::string tag = "tag1"; 1144 syncer::SyncChangeList changes; 1145 1146 std::vector<const SyncedSession*> foreign_sessions; 1147 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions)); 1148 manager()->DeleteForeignSessionInternal(tag, &changes); 1149 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions)); 1150 EXPECT_TRUE(changes.empty()); 1151 1152 // Fill an instance of session specifics with a foreign session's data. 1153 std::vector<sync_pb::SessionSpecifics> tabs; 1154 SessionID::id_type n1[] = {5, 10, 13, 17}; 1155 std::vector<SessionID::id_type> tab_nums1(n1, n1 + arraysize(n1)); 1156 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession( 1157 tag, tab_nums1, &tabs)); 1158 1159 // Update associator with the session's meta node, window, and tabs. 1160 manager()->UpdateTrackerWithForeignSession(meta, base::Time()); 1161 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs.begin(); 1162 iter != tabs.end(); ++iter) { 1163 manager()->UpdateTrackerWithForeignSession(*iter, base::Time()); 1164 } 1165 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1166 ASSERT_EQ(1U, foreign_sessions.size()); 1167 1168 // Now delete the foreign session. 1169 manager()->DeleteForeignSessionInternal(tag, &changes); 1170 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions)); 1171 1172 EXPECT_EQ(5U, changes.size()); 1173 std::set<std::string> expected_tags(&tag, &tag + 1); 1174 for (int i = 0; i < 5; i++) 1175 expected_tags.insert(TabNodePool::TabIdToTag(tag, i)); 1176 1177 for (int i = 0; i < 5; i++) { 1178 SCOPED_TRACE(changes[i].ToString()); 1179 EXPECT_TRUE(changes[i].IsValid()); 1180 EXPECT_EQ(SyncChange::ACTION_DELETE, changes[i].change_type()); 1181 EXPECT_TRUE(changes[i].sync_data().IsValid()); 1182 EXPECT_EQ(1U, 1183 expected_tags.erase( 1184 syncer::SyncDataLocal(changes[i].sync_data()).GetTag())); 1185 } 1186 } 1187 1188 // Write a foreign session to a node, with the tabs arriving first, and then 1189 // retrieve it. 1190 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeTabsFirst) { 1191 InitWithNoSyncData(); 1192 1193 // Fill an instance of session specifics with a foreign session's data. 1194 std::string tag = "tag1"; 1195 SessionID::id_type nums1[] = {5, 10, 13, 17}; 1196 std::vector<sync_pb::SessionSpecifics> tabs1; 1197 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1)); 1198 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession( 1199 tag, tab_list1, &tabs1)); 1200 1201 syncer::SyncChangeList adds; 1202 // Add tabs for first window, then the meta node. 1203 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &adds); 1204 adds.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD)); 1205 manager()->ProcessSyncChanges(FROM_HERE, adds); 1206 1207 // Check that the foreign session was associated and retrieve the data. 1208 std::vector<const SyncedSession*> foreign_sessions; 1209 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1210 ASSERT_EQ(1U, foreign_sessions.size()); 1211 std::vector<std::vector<SessionID::id_type> > session_reference; 1212 session_reference.push_back(tab_list1); 1213 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 1214 } 1215 1216 // Write a foreign session to a node with some tabs that never arrive. 1217 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeMissingTabs) { 1218 InitWithNoSyncData(); 1219 1220 // Fill an instance of session specifics with a foreign session's data. 1221 std::string tag = "tag1"; 1222 SessionID::id_type nums1[] = {5, 10, 13, 17}; 1223 std::vector<sync_pb::SessionSpecifics> tabs1; 1224 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1)); 1225 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession( 1226 tag, tab_list1, &tabs1)); 1227 // Add a second window, but this time only create two tab nodes, despite the 1228 // window expecting four tabs. 1229 SessionID::id_type tab_nums2[] = {7, 15, 18, 20}; 1230 std::vector<SessionID::id_type> tab_list2( 1231 tab_nums2, tab_nums2 + arraysize(tab_nums2)); 1232 helper()->AddWindowSpecifics(1, tab_list2, &meta); 1233 std::vector<sync_pb::SessionSpecifics> tabs2; 1234 tabs2.resize(2); 1235 for (size_t i = 0; i < 2; ++i) { 1236 helper()->BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]); 1237 } 1238 1239 syncer::SyncChangeList changes; 1240 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD)); 1241 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes); 1242 AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes); 1243 manager()->ProcessSyncChanges(FROM_HERE, changes); 1244 changes.clear(); 1245 1246 // Check that the foreign session was associated and retrieve the data. 1247 std::vector<const SyncedSession*> foreign_sessions; 1248 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1249 ASSERT_EQ(1U, foreign_sessions.size()); 1250 ASSERT_EQ(2U, foreign_sessions[0]->windows.size()); 1251 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size()); 1252 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size()); 1253 1254 // Close the second window. 1255 meta.mutable_header()->clear_window(); 1256 helper()->AddWindowSpecifics(0, tab_list1, &meta); 1257 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_UPDATE)); 1258 // Update associator with the session's meta node containing one window. 1259 manager()->ProcessSyncChanges(FROM_HERE, changes); 1260 1261 // Check that the foreign session was associated and retrieve the data. 1262 foreign_sessions.clear(); 1263 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1264 ASSERT_EQ(1U, foreign_sessions.size()); 1265 ASSERT_EQ(1U, foreign_sessions[0]->windows.size()); 1266 std::vector<std::vector<SessionID::id_type> > session_reference; 1267 session_reference.push_back(tab_list1); 1268 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0])); 1269 } 1270 1271 // Tests that the SessionsSyncManager can handle a remote client deleting 1272 // sync nodes that belong to this local session. 1273 TEST_F(SessionsSyncManagerTest, ProcessRemoteDeleteOfLocalSession) { 1274 syncer::SyncChangeList out; 1275 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out); 1276 ASSERT_EQ(2U, out.size()); 1277 sync_pb::EntitySpecifics entity(out[0].sync_data().GetSpecifics()); 1278 SyncData d(SyncData::CreateRemoteData( 1279 1, 1280 entity, 1281 base::Time(), 1282 syncer::AttachmentIdList(), 1283 syncer::AttachmentServiceProxyForTest::Create())); 1284 SetSyncData(syncer::SyncDataList(&d, &d + 1)); 1285 out.clear(); 1286 1287 syncer::SyncChangeList changes; 1288 changes.push_back( 1289 MakeRemoteChange(1, entity.session(), SyncChange::ACTION_DELETE)); 1290 manager()->ProcessSyncChanges(FROM_HERE, changes); 1291 EXPECT_TRUE(manager()->local_tab_pool_out_of_sync_); 1292 EXPECT_TRUE(out.empty()); // ChangeProcessor shouldn't see any activity. 1293 1294 // This should trigger repair of the TabNodePool. 1295 const GURL foo1("http://foo/1"); 1296 AddTab(browser(), foo1); 1297 EXPECT_FALSE(manager()->local_tab_pool_out_of_sync_); 1298 1299 // AddTab triggers two notifications, one for the tab insertion and one for 1300 // committing the NavigationEntry. The first notification results in a tab 1301 // we don't associate although we do update the header node. The second 1302 // notification triggers association of the tab, and the subsequent window 1303 // update. So we should see 4 changes at the SyncChangeProcessor. 1304 ASSERT_EQ(4U, out.size()); 1305 1306 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type()); 1307 ASSERT_TRUE(out[0].sync_data().GetSpecifics().session().has_header()); 1308 EXPECT_EQ(SyncChange::ACTION_ADD, out[1].change_type()); 1309 int tab_node_id = out[1].sync_data().GetSpecifics().session().tab_node_id(); 1310 EXPECT_EQ(TabNodePool::TabIdToTag( 1311 manager()->current_machine_tag(), tab_node_id), 1312 syncer::SyncDataLocal(out[1].sync_data()).GetTag()); 1313 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type()); 1314 ASSERT_TRUE(out[2].sync_data().GetSpecifics().session().has_tab()); 1315 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[3].change_type()); 1316 ASSERT_TRUE(out[3].sync_data().GetSpecifics().session().has_header()); 1317 1318 // Verify the actual content. 1319 const sync_pb::SessionHeader& session_header = 1320 out[3].sync_data().GetSpecifics().session().header(); 1321 ASSERT_EQ(1, session_header.window_size()); 1322 EXPECT_EQ(1, session_header.window(0).tab_size()); 1323 const sync_pb::SessionTab& tab1 = 1324 out[2].sync_data().GetSpecifics().session().tab(); 1325 ASSERT_EQ(1, tab1.navigation_size()); 1326 EXPECT_EQ(foo1.spec(), tab1.navigation(0).virtual_url()); 1327 1328 // Verify TabNodePool integrity. 1329 EXPECT_EQ(1U, manager()->local_tab_pool_.Capacity()); 1330 EXPECT_TRUE(manager()->local_tab_pool_.Empty()); 1331 1332 // Verify TabLinks. 1333 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_; 1334 ASSERT_EQ(1U, tab_map.size()); 1335 int tab_id = out[2].sync_data().GetSpecifics().session().tab().tab_id(); 1336 EXPECT_EQ(tab_node_id, tab_map.find(tab_id)->second->tab_node_id()); 1337 } 1338 1339 // Test that receiving a session delete from sync removes the session 1340 // from tracking. 1341 TEST_F(SessionsSyncManagerTest, ProcessForeignDelete) { 1342 InitWithNoSyncData(); 1343 SessionID::id_type n[] = {5}; 1344 std::vector<sync_pb::SessionSpecifics> tabs1; 1345 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n)); 1346 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession( 1347 "tag1", tab_list, &tabs1)); 1348 1349 syncer::SyncChangeList changes; 1350 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD)); 1351 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes); 1352 manager()->ProcessSyncChanges(FROM_HERE, changes); 1353 1354 std::vector<const SyncedSession*> foreign_sessions; 1355 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1356 ASSERT_EQ(1U, foreign_sessions.size()); 1357 1358 changes.clear(); 1359 foreign_sessions.clear(); 1360 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE)); 1361 manager()->ProcessSyncChanges(FROM_HERE, changes); 1362 1363 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions)); 1364 } 1365 1366 // TODO(shashishekhar): "Move this to TabNodePool unittests." 1367 TEST_F(SessionsSyncManagerTest, SaveUnassociatedNodesForReassociation) { 1368 syncer::SyncChangeList changes; 1369 InitWithNoSyncData(); 1370 1371 std::string local_tag = manager()->current_machine_tag(); 1372 // Create a free node and then dissassociate sessions so that it ends up 1373 // unassociated. 1374 manager()->local_tab_pool_.GetFreeTabNode(&changes); 1375 1376 // Update the tab_id of the node, so that it is considered a valid 1377 // unassociated node otherwise it will be mistaken for a corrupted node and 1378 // will be deleted before being added to the tab node pool. 1379 sync_pb::EntitySpecifics entity(changes[0].sync_data().GetSpecifics()); 1380 entity.mutable_session()->mutable_tab()->set_tab_id(1); 1381 SyncData d(SyncData::CreateRemoteData( 1382 1, 1383 entity, 1384 base::Time(), 1385 syncer::AttachmentIdList(), 1386 syncer::AttachmentServiceProxyForTest::Create())); 1387 syncer::SyncDataList in(&d, &d + 1); 1388 changes.clear(); 1389 SessionsSyncManager manager2(profile(), local_device(), NewDummyRouter()); 1390 syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing( 1391 syncer::SESSIONS, in, 1392 scoped_ptr<syncer::SyncChangeProcessor>( 1393 new TestSyncProcessorStub(&changes)), 1394 scoped_ptr<syncer::SyncErrorFactory>( 1395 new syncer::SyncErrorFactoryMock())); 1396 ASSERT_FALSE(result.error().IsSet()); 1397 EXPECT_TRUE(FilterOutLocalHeaderChanges(&changes)->empty()); 1398 } 1399 1400 TEST_F(SessionsSyncManagerTest, MergeDeletesCorruptNode) { 1401 syncer::SyncChangeList changes; 1402 InitWithNoSyncData(); 1403 1404 std::string local_tag = manager()->current_machine_tag(); 1405 int tab_node_id = manager()->local_tab_pool_.GetFreeTabNode(&changes); 1406 SyncData d(SyncData::CreateRemoteData( 1407 1, 1408 changes[0].sync_data().GetSpecifics(), 1409 base::Time(), 1410 syncer::AttachmentIdList(), 1411 syncer::AttachmentServiceProxyForTest::Create())); 1412 syncer::SyncDataList in(&d, &d + 1); 1413 changes.clear(); 1414 TearDown(); 1415 SetUp(); 1416 InitWithSyncDataTakeOutput(in, &changes); 1417 EXPECT_EQ(1U, FilterOutLocalHeaderChanges(&changes)->size()); 1418 EXPECT_EQ(SyncChange::ACTION_DELETE, changes[0].change_type()); 1419 EXPECT_EQ(TabNodePool::TabIdToTag(local_tag, tab_node_id), 1420 syncer::SyncDataLocal(changes[0].sync_data()).GetTag()); 1421 } 1422 1423 // Test that things work if a tab is initially ignored. 1424 TEST_F(SessionsSyncManagerTest, AssociateWindowsDontReloadTabs) { 1425 syncer::SyncChangeList out; 1426 // Go to a URL that is ignored by session syncing. 1427 AddTab(browser(), GURL("chrome://preferences/")); 1428 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out); 1429 ASSERT_EQ(2U, out.size()); // Header add and update. 1430 EXPECT_EQ( 1431 0, 1432 out[1].sync_data().GetSpecifics().session().header().window_size()); 1433 out.clear(); 1434 1435 // Go to a sync-interesting URL. 1436 NavigateAndCommitActiveTab(GURL("http://foo2")); 1437 1438 EXPECT_EQ(3U, out.size()); // Tab add, update, and header update. 1439 1440 EXPECT_TRUE( 1441 StartsWithASCII(syncer::SyncDataLocal(out[0].sync_data()).GetTag(), 1442 manager()->current_machine_tag(), 1443 true)); 1444 EXPECT_EQ(manager()->current_machine_tag(), 1445 out[0].sync_data().GetSpecifics().session().session_tag()); 1446 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type()); 1447 1448 EXPECT_TRUE( 1449 StartsWithASCII(syncer::SyncDataLocal(out[1].sync_data()).GetTag(), 1450 manager()->current_machine_tag(), 1451 true)); 1452 EXPECT_EQ(manager()->current_machine_tag(), 1453 out[1].sync_data().GetSpecifics().session().session_tag()); 1454 EXPECT_TRUE(out[1].sync_data().GetSpecifics().session().has_tab()); 1455 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type()); 1456 1457 EXPECT_TRUE(out[2].IsValid()); 1458 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type()); 1459 const SyncData data(out[2].sync_data()); 1460 EXPECT_EQ(manager()->current_machine_tag(), 1461 syncer::SyncDataLocal(data).GetTag()); 1462 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session()); 1463 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag()); 1464 EXPECT_TRUE(specifics.has_header()); 1465 const sync_pb::SessionHeader& header_s = specifics.header(); 1466 EXPECT_EQ(1, header_s.window_size()); 1467 EXPECT_EQ(1, header_s.window(0).tab_size()); 1468 } 1469 1470 // Tests that the SyncSessionManager responds to local tab events properly. 1471 TEST_F(SessionsSyncManagerTest, OnLocalTabModified) { 1472 syncer::SyncChangeList out; 1473 // Init with no local data, relies on MergeLocalSessionNoTabs. 1474 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out); 1475 ASSERT_FALSE(manager()->current_machine_tag().empty()); 1476 ASSERT_EQ(2U, out.size()); 1477 1478 // Copy the original header. 1479 sync_pb::EntitySpecifics header(out[0].sync_data().GetSpecifics()); 1480 out.clear(); 1481 1482 const GURL foo1("http://foo/1"); 1483 const GURL foo2("http://foo/2"); 1484 const GURL bar1("http://bar/1"); 1485 const GURL bar2("http://bar/2"); 1486 AddTab(browser(), foo1); 1487 NavigateAndCommitActiveTab(foo2); 1488 AddTab(browser(), bar1); 1489 NavigateAndCommitActiveTab(bar2); 1490 1491 // One add, one update for each AddTab. 1492 // One update for each NavigateAndCommit. 1493 // = 6 total tab updates. 1494 // One header update corresponding to each of those. 1495 // = 6 total header updates. 1496 // 12 total updates. 1497 ASSERT_EQ(12U, out.size()); 1498 1499 // Verify the tab node creations and updates to ensure the SyncProcessor 1500 // sees the right operations. 1501 for (int i = 0; i < 12; i++) { 1502 SCOPED_TRACE(i); 1503 EXPECT_TRUE(out[i].IsValid()); 1504 const SyncData data(out[i].sync_data()); 1505 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(), 1506 manager()->current_machine_tag(), true)); 1507 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session()); 1508 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag()); 1509 if (i % 6 == 0) { 1510 // First thing on an AddTab is a no-op header update for parented tab. 1511 EXPECT_EQ(header.SerializeAsString(), 1512 data.GetSpecifics().SerializeAsString()); 1513 EXPECT_EQ(manager()->current_machine_tag(), 1514 syncer::SyncDataLocal(data).GetTag()); 1515 } else if (i % 6 == 1) { 1516 // Next, the TabNodePool should create the tab node. 1517 EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type()); 1518 EXPECT_EQ(TabNodePool::TabIdToTag( 1519 manager()->current_machine_tag(), 1520 data.GetSpecifics().session().tab_node_id()), 1521 syncer::SyncDataLocal(data).GetTag()); 1522 } else if (i % 6 == 2) { 1523 // Then we see the tab update to the URL. 1524 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type()); 1525 EXPECT_EQ(TabNodePool::TabIdToTag( 1526 manager()->current_machine_tag(), 1527 data.GetSpecifics().session().tab_node_id()), 1528 syncer::SyncDataLocal(data).GetTag()); 1529 ASSERT_TRUE(specifics.has_tab()); 1530 } else if (i % 6 == 3) { 1531 // The header needs to be updated to reflect the new window state. 1532 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type()); 1533 EXPECT_TRUE(specifics.has_header()); 1534 } else if (i % 6 == 4) { 1535 // Now we move on to NavigateAndCommit. Update the tab. 1536 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type()); 1537 EXPECT_EQ(TabNodePool::TabIdToTag( 1538 manager()->current_machine_tag(), 1539 data.GetSpecifics().session().tab_node_id()), 1540 syncer::SyncDataLocal(data).GetTag()); 1541 ASSERT_TRUE(specifics.has_tab()); 1542 } else if (i % 6 == 5) { 1543 // The header needs to be updated to reflect the new window state. 1544 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type()); 1545 ASSERT_TRUE(specifics.has_header()); 1546 header = data.GetSpecifics(); 1547 } 1548 } 1549 1550 // Verify the actual content to ensure sync sees the right data. 1551 // When it's all said and done, the header should reflect two tabs. 1552 const sync_pb::SessionHeader& session_header = header.session().header(); 1553 ASSERT_EQ(1, session_header.window_size()); 1554 EXPECT_EQ(2, session_header.window(0).tab_size()); 1555 1556 // ASSERT_TRUEs above allow us to dive in freely here. 1557 // Verify first tab. 1558 const sync_pb::SessionTab& tab1_1 = 1559 out[2].sync_data().GetSpecifics().session().tab(); 1560 ASSERT_EQ(1, tab1_1.navigation_size()); 1561 EXPECT_EQ(foo1.spec(), tab1_1.navigation(0).virtual_url()); 1562 const sync_pb::SessionTab& tab1_2 = 1563 out[4].sync_data().GetSpecifics().session().tab(); 1564 ASSERT_EQ(2, tab1_2.navigation_size()); 1565 EXPECT_EQ(foo1.spec(), tab1_2.navigation(0).virtual_url()); 1566 EXPECT_EQ(foo2.spec(), tab1_2.navigation(1).virtual_url()); 1567 1568 // Verify second tab. 1569 const sync_pb::SessionTab& tab2_1 = 1570 out[8].sync_data().GetSpecifics().session().tab(); 1571 ASSERT_EQ(1, tab2_1.navigation_size()); 1572 EXPECT_EQ(bar1.spec(), tab2_1.navigation(0).virtual_url()); 1573 const sync_pb::SessionTab& tab2_2 = 1574 out[10].sync_data().GetSpecifics().session().tab(); 1575 ASSERT_EQ(2, tab2_2.navigation_size()); 1576 EXPECT_EQ(bar1.spec(), tab2_2.navigation(0).virtual_url()); 1577 EXPECT_EQ(bar2.spec(), tab2_2.navigation(1).virtual_url()); 1578 } 1579 1580 // Ensure model association associates the pre-existing tabs. 1581 TEST_F(SessionsSyncManagerTest, MergeLocalSessionExistingTabs) { 1582 AddTab(browser(), GURL("http://foo1")); 1583 NavigateAndCommitActiveTab(GURL("http://foo2")); // Adds back entry. 1584 AddTab(browser(), GURL("http://bar1")); 1585 NavigateAndCommitActiveTab(GURL("http://bar2")); // Adds back entry. 1586 1587 syncer::SyncChangeList out; 1588 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out); 1589 ASSERT_EQ(6U, out.size()); 1590 1591 // Check that this machine's data is not included in the foreign windows. 1592 std::vector<const SyncedSession*> foreign_sessions; 1593 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions)); 1594 1595 // Verify the header. 1596 EXPECT_TRUE(out[0].IsValid()); 1597 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type()); 1598 const SyncData data(out[0].sync_data()); 1599 EXPECT_EQ(manager()->current_machine_tag(), 1600 syncer::SyncDataLocal(data).GetTag()); 1601 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session()); 1602 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag()); 1603 EXPECT_TRUE(specifics.has_header()); 1604 const sync_pb::SessionHeader& header_s = specifics.header(); 1605 EXPECT_TRUE(header_s.has_device_type()); 1606 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name()); 1607 EXPECT_EQ(0, header_s.window_size()); 1608 1609 // Verify the tab node creations and updates with content. 1610 for (int i = 1; i < 5; i++) { 1611 EXPECT_TRUE(out[i].IsValid()); 1612 const SyncData data(out[i].sync_data()); 1613 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(), 1614 manager()->current_machine_tag(), true)); 1615 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session()); 1616 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag()); 1617 if (i % 2 == 1) { 1618 EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type()); 1619 } else { 1620 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type()); 1621 EXPECT_TRUE(specifics.has_tab()); 1622 } 1623 } 1624 1625 // Verify the header was updated to reflect new window state. 1626 EXPECT_TRUE(out[5].IsValid()); 1627 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type()); 1628 const SyncData data_2(out[5].sync_data()); 1629 EXPECT_EQ(manager()->current_machine_tag(), 1630 syncer::SyncDataLocal(data_2).GetTag()); 1631 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session()); 1632 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag()); 1633 EXPECT_TRUE(specifics2.has_header()); 1634 const sync_pb::SessionHeader& header_s2 = specifics2.header(); 1635 EXPECT_EQ(1, header_s2.window_size()); 1636 1637 // Verify TabLinks. 1638 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_; 1639 ASSERT_EQ(2U, tab_map.size()); 1640 // Tabs are ordered by sessionid in tab_map, so should be able to traverse 1641 // the tree based on order of tabs created 1642 SessionsSyncManager::TabLinksMap::iterator iter = tab_map.begin(); 1643 ASSERT_EQ(2, iter->second->tab()->GetEntryCount()); 1644 EXPECT_EQ(GURL("http://foo1"), iter->second->tab()-> 1645 GetEntryAtIndex(0)->GetVirtualURL()); 1646 EXPECT_EQ(GURL("http://foo2"), iter->second->tab()-> 1647 GetEntryAtIndex(1)->GetVirtualURL()); 1648 iter++; 1649 ASSERT_EQ(2, iter->second->tab()->GetEntryCount()); 1650 EXPECT_EQ(GURL("http://bar1"), iter->second->tab()-> 1651 GetEntryAtIndex(0)->GetVirtualURL()); 1652 EXPECT_EQ(GURL("http://bar2"), iter->second->tab()-> 1653 GetEntryAtIndex(1)->GetVirtualURL()); 1654 } 1655 1656 // Test garbage collection of stale foreign sessions. 1657 TEST_F(SessionsSyncManagerTest, DoGarbageCollection) { 1658 // Fill two instances of session specifics with a foreign session's data. 1659 std::string tag1 = "tag1"; 1660 SessionID::id_type n1[] = {5, 10, 13, 17}; 1661 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1)); 1662 std::vector<sync_pb::SessionSpecifics> tabs1; 1663 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession( 1664 tag1, tab_list1, &tabs1)); 1665 std::string tag2 = "tag2"; 1666 SessionID::id_type n2[] = {8, 15, 18, 20}; 1667 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2)); 1668 std::vector<sync_pb::SessionSpecifics> tabs2; 1669 sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession( 1670 tag2, tab_list2, &tabs2)); 1671 // Set the modification time for tag1 to be 21 days ago, tag2 to 5 days ago. 1672 base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21); 1673 base::Time tag2_time = base::Time::Now() - base::TimeDelta::FromDays(5); 1674 1675 syncer::SyncDataList foreign_data; 1676 sync_pb::EntitySpecifics entity1, entity2; 1677 entity1.mutable_session()->CopyFrom(meta); 1678 entity2.mutable_session()->CopyFrom(meta2); 1679 foreign_data.push_back(SyncData::CreateRemoteData( 1680 1, 1681 entity1, 1682 tag1_time, 1683 syncer::AttachmentIdList(), 1684 syncer::AttachmentServiceProxyForTest::Create())); 1685 foreign_data.push_back(SyncData::CreateRemoteData( 1686 1, 1687 entity2, 1688 tag2_time, 1689 syncer::AttachmentIdList(), 1690 syncer::AttachmentServiceProxyForTest::Create())); 1691 AddTabsToSyncDataList(tabs1, &foreign_data); 1692 AddTabsToSyncDataList(tabs2, &foreign_data); 1693 1694 syncer::SyncChangeList output; 1695 InitWithSyncDataTakeOutput(foreign_data, &output); 1696 ASSERT_EQ(2U, output.size()); 1697 output.clear(); 1698 1699 // Check that the foreign session was associated and retrieve the data. 1700 std::vector<const SyncedSession*> foreign_sessions; 1701 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1702 ASSERT_EQ(2U, foreign_sessions.size()); 1703 foreign_sessions.clear(); 1704 1705 // Now garbage collect and verify the non-stale session is still there. 1706 manager()->DoGarbageCollection(); 1707 ASSERT_EQ(5U, output.size()); 1708 EXPECT_EQ(SyncChange::ACTION_DELETE, output[0].change_type()); 1709 const SyncData data(output[0].sync_data()); 1710 EXPECT_EQ(tag1, syncer::SyncDataLocal(data).GetTag()); 1711 for (int i = 1; i < 5; i++) { 1712 EXPECT_EQ(SyncChange::ACTION_DELETE, output[i].change_type()); 1713 const SyncData data(output[i].sync_data()); 1714 EXPECT_EQ(TabNodePool::TabIdToTag(tag1, i), 1715 syncer::SyncDataLocal(data).GetTag()); 1716 } 1717 1718 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1719 ASSERT_EQ(1U, foreign_sessions.size()); 1720 std::vector<std::vector<SessionID::id_type> > session_reference; 1721 session_reference.push_back(tab_list2); 1722 helper()->VerifySyncedSession(tag2, session_reference, 1723 *(foreign_sessions[0])); 1724 } 1725 1726 // Test that an update to a previously considered "stale" session, 1727 // prior to garbage collection, will save the session from deletion. 1728 TEST_F(SessionsSyncManagerTest, GarbageCollectionHonoursUpdate) { 1729 std::string tag1 = "tag1"; 1730 SessionID::id_type n1[] = {5, 10, 13, 17}; 1731 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1)); 1732 std::vector<sync_pb::SessionSpecifics> tabs1; 1733 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession( 1734 tag1, tab_list1, &tabs1)); 1735 syncer::SyncDataList foreign_data; 1736 sync_pb::EntitySpecifics entity1; 1737 base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21); 1738 entity1.mutable_session()->CopyFrom(meta); 1739 foreign_data.push_back(SyncData::CreateRemoteData( 1740 1, 1741 entity1, 1742 tag1_time, 1743 syncer::AttachmentIdList(), 1744 syncer::AttachmentServiceProxyForTest::Create())); 1745 AddTabsToSyncDataList(tabs1, &foreign_data); 1746 syncer::SyncChangeList output; 1747 InitWithSyncDataTakeOutput(foreign_data, &output); 1748 ASSERT_EQ(2U, output.size()); 1749 1750 // Update to a non-stale time. 1751 sync_pb::EntitySpecifics update_entity; 1752 update_entity.mutable_session()->CopyFrom(tabs1[0]); 1753 syncer::SyncChangeList changes; 1754 changes.push_back( 1755 syncer::SyncChange(FROM_HERE, 1756 SyncChange::ACTION_UPDATE, 1757 syncer::SyncData::CreateRemoteData( 1758 1, 1759 update_entity, 1760 base::Time::Now(), 1761 syncer::AttachmentIdList(), 1762 syncer::AttachmentServiceProxyForTest::Create()))); 1763 manager()->ProcessSyncChanges(FROM_HERE, changes); 1764 1765 // Check that the foreign session was associated and retrieve the data. 1766 std::vector<const SyncedSession*> foreign_sessions; 1767 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1768 ASSERT_EQ(1U, foreign_sessions.size()); 1769 foreign_sessions.clear(); 1770 1771 // Verify the now non-stale session does not get deleted. 1772 manager()->DoGarbageCollection(); 1773 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions)); 1774 ASSERT_EQ(1U, foreign_sessions.size()); 1775 std::vector<std::vector<SessionID::id_type> > session_reference; 1776 session_reference.push_back(tab_list1); 1777 helper()->VerifySyncedSession( 1778 tag1, session_reference, *(foreign_sessions[0])); 1779 } 1780 1781 // Test that swapping WebContents for a tab is properly observed and handled 1782 // by the SessionsSyncManager. 1783 TEST_F(SessionsSyncManagerTest, CheckPrerenderedWebContentsSwap) { 1784 AddTab(browser(), GURL("http://foo1")); 1785 NavigateAndCommitActiveTab(GURL("http://foo2")); 1786 1787 syncer::SyncChangeList out; 1788 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out); 1789 ASSERT_EQ(4U, out.size()); // Header, tab ADD, tab UPDATE, header UPDATE. 1790 1791 // To simulate WebContents swap during prerendering, create new WebContents 1792 // and swap with old WebContents. 1793 scoped_ptr<content::WebContents> old_web_contents; 1794 old_web_contents.reset(browser()->tab_strip_model()->GetActiveWebContents()); 1795 1796 // Create new WebContents, with the required tab helpers. 1797 WebContents* new_web_contents = WebContents::CreateWithSessionStorage( 1798 WebContents::CreateParams(profile()), 1799 old_web_contents->GetController().GetSessionStorageNamespaceMap()); 1800 SessionTabHelper::CreateForWebContents(new_web_contents); 1801 TabContentsSyncedTabDelegate::CreateForWebContents(new_web_contents); 1802 new_web_contents->GetController() 1803 .CopyStateFrom(old_web_contents->GetController()); 1804 1805 // Swap the WebContents. 1806 int index = browser()->tab_strip_model()->GetIndexOfWebContents( 1807 old_web_contents.get()); 1808 browser()->tab_strip_model()->ReplaceWebContentsAt(index, new_web_contents); 1809 1810 ASSERT_EQ(9U, out.size()); 1811 EXPECT_EQ(SyncChange::ACTION_ADD, out[4].change_type()); 1812 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type()); 1813 1814 // Navigate away. 1815 NavigateAndCommitActiveTab(GURL("http://bar2")); 1816 1817 // Delete old WebContents. This should not crash. 1818 old_web_contents.reset(); 1819 1820 // Try more navigations and verify output size. This can also reveal 1821 // bugs (leaks) on memcheck bots if the SessionSyncManager 1822 // didn't properly clean up the tab pool or session tracker. 1823 NavigateAndCommitActiveTab(GURL("http://bar3")); 1824 1825 AddTab(browser(), GURL("http://bar4")); 1826 NavigateAndCommitActiveTab(GURL("http://bar5")); 1827 ASSERT_EQ(19U, out.size()); 1828 } 1829 1830 namespace { 1831 class SessionNotificationObserver : public content::NotificationObserver { 1832 public: 1833 SessionNotificationObserver() : notified_of_update_(false), 1834 notified_of_refresh_(false) { 1835 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED, 1836 content::NotificationService::AllSources()); 1837 registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL, 1838 content::NotificationService::AllSources()); 1839 } 1840 virtual void Observe(int type, 1841 const content::NotificationSource& source, 1842 const content::NotificationDetails& details) OVERRIDE { 1843 switch (type) { 1844 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED: 1845 notified_of_update_ = true; 1846 break; 1847 case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL: 1848 notified_of_refresh_ = true; 1849 break; 1850 default: 1851 NOTREACHED(); 1852 break; 1853 } 1854 } 1855 bool notified_of_update() const { return notified_of_update_; } 1856 bool notified_of_refresh() const { return notified_of_refresh_; } 1857 void Reset() { 1858 notified_of_update_ = false; 1859 notified_of_refresh_ = false; 1860 } 1861 private: 1862 content::NotificationRegistrar registrar_; 1863 bool notified_of_update_; 1864 bool notified_of_refresh_; 1865 }; 1866 } // namespace 1867 1868 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when processing 1869 // sync changes. 1870 TEST_F(SessionsSyncManagerTest, NotifiedOfUpdates) { 1871 SessionNotificationObserver observer; 1872 ASSERT_FALSE(observer.notified_of_update()); 1873 InitWithNoSyncData(); 1874 1875 SessionID::id_type n[] = {5}; 1876 std::vector<sync_pb::SessionSpecifics> tabs1; 1877 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n)); 1878 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession( 1879 "tag1", tab_list, &tabs1)); 1880 1881 syncer::SyncChangeList changes; 1882 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD)); 1883 manager()->ProcessSyncChanges(FROM_HERE, changes); 1884 EXPECT_TRUE(observer.notified_of_update()); 1885 1886 changes.clear(); 1887 observer.Reset(); 1888 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes); 1889 manager()->ProcessSyncChanges(FROM_HERE, changes); 1890 EXPECT_TRUE(observer.notified_of_update()); 1891 1892 changes.clear(); 1893 observer.Reset(); 1894 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE)); 1895 manager()->ProcessSyncChanges(FROM_HERE, changes); 1896 EXPECT_TRUE(observer.notified_of_update()); 1897 } 1898 1899 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when handling 1900 // local hide/removal of foreign session. 1901 TEST_F(SessionsSyncManagerTest, NotifiedOfLocalRemovalOfForeignSession) { 1902 InitWithNoSyncData(); 1903 const std::string tag("tag1"); 1904 SessionID::id_type n[] = {5}; 1905 std::vector<sync_pb::SessionSpecifics> tabs1; 1906 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n)); 1907 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession( 1908 tag, tab_list, &tabs1)); 1909 1910 syncer::SyncChangeList changes; 1911 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD)); 1912 manager()->ProcessSyncChanges(FROM_HERE, changes); 1913 1914 SessionNotificationObserver observer; 1915 ASSERT_FALSE(observer.notified_of_update()); 1916 manager()->DeleteForeignSession(tag); 1917 ASSERT_TRUE(observer.notified_of_update()); 1918 } 1919 1920 #if defined(OS_ANDROID) || defined(OS_IOS) 1921 // Tests that opening the other devices page triggers a session sync refresh. 1922 // This page only exists on mobile platforms today; desktop has a 1923 // search-enhanced NTP without other devices. 1924 TEST_F(SessionsSyncManagerTest, NotifiedOfRefresh) { 1925 SessionNotificationObserver observer; 1926 ASSERT_FALSE(observer.notified_of_refresh()); 1927 InitWithNoSyncData(); 1928 AddTab(browser(), GURL("http://foo1")); 1929 EXPECT_FALSE(observer.notified_of_refresh()); 1930 NavigateAndCommitActiveTab(GURL("chrome://newtab/#open_tabs")); 1931 EXPECT_TRUE(observer.notified_of_refresh()); 1932 } 1933 #endif // defined(OS_ANDROID) || defined(OS_IOS) 1934 1935 // Tests receipt of duplicate tab IDs in the same window. This should never 1936 // happen, but we want to make sure the client won't do anything bad if it does 1937 // receive such garbage input data. 1938 TEST_F(SessionsSyncManagerTest, ReceiveDuplicateTabInSameWindow) { 1939 std::string tag = "tag1"; 1940 1941 // Reuse tab ID 10 in an attempt to trigger bad behavior. 1942 SessionID::id_type n1[] = {5, 10, 10, 17}; 1943 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1)); 1944 std::vector<sync_pb::SessionSpecifics> tabs1; 1945 sync_pb::SessionSpecifics meta( 1946 helper()->BuildForeignSession(tag, tab_list1, &tabs1)); 1947 1948 // Set up initial data. 1949 syncer::SyncDataList initial_data; 1950 sync_pb::EntitySpecifics entity; 1951 entity.mutable_session()->CopyFrom(meta); 1952 initial_data.push_back(SyncData::CreateRemoteData( 1953 1, 1954 entity, 1955 base::Time(), 1956 syncer::AttachmentIdList(), 1957 syncer::AttachmentServiceProxyForTest::Create())); 1958 AddTabsToSyncDataList(tabs1, &initial_data); 1959 1960 syncer::SyncChangeList output; 1961 InitWithSyncDataTakeOutput(initial_data, &output); 1962 } 1963 1964 // Tests receipt of duplicate tab IDs for the same session. The duplicate tab 1965 // ID is present in two different windows. A client can't be expected to do 1966 // anything reasonable with this input, but we can expect that it doesn't 1967 // crash. 1968 TEST_F(SessionsSyncManagerTest, ReceiveDuplicateTabInOtherWindow) { 1969 std::string tag = "tag1"; 1970 1971 SessionID::id_type n1[] = {5, 10, 17}; 1972 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1)); 1973 std::vector<sync_pb::SessionSpecifics> tabs1; 1974 sync_pb::SessionSpecifics meta( 1975 helper()->BuildForeignSession(tag, tab_list1, &tabs1)); 1976 1977 // Add a second window. Tab ID 10 is a duplicate. 1978 SessionID::id_type n2[] = {10, 18, 20}; 1979 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2)); 1980 helper()->AddWindowSpecifics(1, tab_list2, &meta); 1981 1982 // Set up initial data. 1983 syncer::SyncDataList initial_data; 1984 sync_pb::EntitySpecifics entity; 1985 entity.mutable_session()->CopyFrom(meta); 1986 initial_data.push_back(SyncData::CreateRemoteData( 1987 1, 1988 entity, 1989 base::Time(), 1990 syncer::AttachmentIdList(), 1991 syncer::AttachmentServiceProxyForTest::Create())); 1992 AddTabsToSyncDataList(tabs1, &initial_data); 1993 1994 for (size_t i = 0; i < tab_list2.size(); ++i) { 1995 sync_pb::EntitySpecifics entity; 1996 helper()->BuildTabSpecifics(tag, 0, tab_list2[i], entity.mutable_session()); 1997 initial_data.push_back(SyncData::CreateRemoteData( 1998 i + 10, 1999 entity, 2000 base::Time(), 2001 syncer::AttachmentIdList(), 2002 syncer::AttachmentServiceProxyForTest::Create())); 2003 } 2004 2005 syncer::SyncChangeList output; 2006 InitWithSyncDataTakeOutput(initial_data, &output); 2007 } 2008 2009 } // namespace browser_sync 2010