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.
5 #include "chrome/browser/sync/sessions/sessions_sync_manager.h"
7 #include "base/strings/string_util.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/sessions/session_id.h"
10 #include "chrome/browser/sessions/session_tab_helper.h"
11 #include "chrome/browser/sessions/session_types.h"
12 #include "chrome/browser/sync/glue/device_info.h"
13 #include "chrome/browser/sync/glue/session_sync_test_helper.h"
14 #include "chrome/browser/sync/glue/synced_tab_delegate.h"
15 #include "chrome/browser/sync/glue/synced_window_delegate.h"
16 #include "chrome/browser/sync/sessions/notification_service_sessions_router.h"
17 #include "chrome/browser/sync/sessions/sessions_util.h"
18 #include "chrome/browser/sync/sessions/synced_window_delegates_getter.h"
19 #include "chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h"
20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
21 #include "chrome/test/base/browser_with_test_window_test.h"
22 #include "components/sessions/serialized_navigation_entry_test_helper.h"
23 #include "content/public/browser/navigation_entry.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_source.h"
27 #include "content/public/browser/web_contents.h"
28 #include "sync/api/attachments/attachment_id.h"
29 #include "sync/api/attachments/attachment_service_proxy_for_test.h"
30 #include "sync/api/sync_error_factory_mock.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
34 using content::WebContents;
35 using sessions::SerializedNavigationEntry;
36 using sessions::SerializedNavigationEntryTestHelper;
37 using syncer::SyncChange;
38 using syncer::SyncData;
40 namespace browser_sync {
44 class SyncedWindowDelegateOverride : public SyncedWindowDelegate {
46 explicit SyncedWindowDelegateOverride(SyncedWindowDelegate* wrapped)
49 virtual ~SyncedWindowDelegateOverride() {}
51 virtual bool HasWindow() const OVERRIDE {
52 return wrapped_->HasWindow();
55 virtual SessionID::id_type GetSessionId() const OVERRIDE {
56 return wrapped_->GetSessionId();
59 virtual int GetTabCount() const OVERRIDE {
60 return wrapped_->GetTabCount();
63 virtual int GetActiveIndex() const OVERRIDE {
64 return wrapped_->GetActiveIndex();
67 virtual bool IsApp() const OVERRIDE {
68 return wrapped_->IsApp();
71 virtual bool IsTypeTabbed() const OVERRIDE {
72 return wrapped_->IsTypeTabbed();
75 virtual bool IsTypePopup() const OVERRIDE {
76 return wrapped_->IsTypePopup();
79 virtual bool IsTabPinned(const SyncedTabDelegate* tab) const OVERRIDE {
80 return wrapped_->IsTabPinned(tab);
83 virtual SyncedTabDelegate* GetTabAt(int index) const OVERRIDE {
84 if (tab_overrides_.find(index) != tab_overrides_.end())
85 return tab_overrides_.find(index)->second;
87 return wrapped_->GetTabAt(index);
90 void OverrideTabAt(int index,
91 SyncedTabDelegate* delegate,
92 SessionID::id_type tab_id) {
93 tab_overrides_[index] = delegate;
94 tab_id_overrides_[index] = tab_id;
97 virtual SessionID::id_type GetTabIdAt(int index) const OVERRIDE {
98 if (tab_id_overrides_.find(index) != tab_id_overrides_.end())
99 return tab_id_overrides_.find(index)->second;
100 return wrapped_->GetTabIdAt(index);
103 virtual bool IsSessionRestoreInProgress() const OVERRIDE {
104 return wrapped_->IsSessionRestoreInProgress();
108 std::map<int, SyncedTabDelegate*> tab_overrides_;
109 std::map<int, SessionID::id_type> tab_id_overrides_;
110 SyncedWindowDelegate* wrapped_;
113 class TestSyncedWindowDelegatesGetter : public SyncedWindowDelegatesGetter {
115 TestSyncedWindowDelegatesGetter(
116 const std::set<SyncedWindowDelegate*>& delegates)
117 : delegates_(delegates) {}
119 virtual const std::set<SyncedWindowDelegate*> GetSyncedWindowDelegates()
124 const std::set<SyncedWindowDelegate*> delegates_;
127 class TestSyncProcessorStub : public syncer::SyncChangeProcessor {
129 explicit TestSyncProcessorStub(syncer::SyncChangeList* output)
131 virtual syncer::SyncError ProcessSyncChanges(
132 const tracked_objects::Location& from_here,
133 const syncer::SyncChangeList& change_list) OVERRIDE {
134 if (error_.IsSet()) {
135 syncer::SyncError error = error_;
136 error_ = syncer::SyncError();
141 output_->insert(output_->end(), change_list.begin(), change_list.end());
143 return syncer::SyncError();
146 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type)
148 return sync_data_to_return_;
151 void FailProcessSyncChangesWith(const syncer::SyncError& error) {
155 void SetSyncDataToReturn(const syncer::SyncDataList& data) {
156 sync_data_to_return_ = data;
160 syncer::SyncError error_;
161 syncer::SyncChangeList* output_;
162 syncer::SyncDataList sync_data_to_return_;
165 syncer::SyncChange MakeRemoteChange(
167 const sync_pb::SessionSpecifics& specifics,
168 SyncChange::SyncChangeType type) {
169 sync_pb::EntitySpecifics entity;
170 entity.mutable_session()->CopyFrom(specifics);
171 return syncer::SyncChange(
174 syncer::SyncData::CreateRemoteData(
178 syncer::AttachmentIdList(),
179 syncer::AttachmentServiceProxyForTest::Create()));
182 void AddTabsToChangeList(
183 const std::vector<sync_pb::SessionSpecifics>& batch,
184 SyncChange::SyncChangeType type,
185 syncer::SyncChangeList* change_list) {
186 std::vector<sync_pb::SessionSpecifics>::const_iterator iter;
187 for (iter = batch.begin();
188 iter != batch.end(); ++iter) {
189 sync_pb::EntitySpecifics entity;
190 entity.mutable_session()->CopyFrom(*iter);
191 change_list->push_back(syncer::SyncChange(
194 syncer::SyncData::CreateRemoteData(
198 syncer::AttachmentIdList(),
199 syncer::AttachmentServiceProxyForTest::Create())));
203 void AddTabsToSyncDataList(const std::vector<sync_pb::SessionSpecifics> tabs,
204 syncer::SyncDataList* list) {
205 for (size_t i = 0; i < tabs.size(); i++) {
206 sync_pb::EntitySpecifics entity;
207 entity.mutable_session()->CopyFrom(tabs[i]);
208 list->push_back(SyncData::CreateRemoteData(
212 syncer::AttachmentIdList(),
213 syncer::AttachmentServiceProxyForTest::Create()));
217 class DummyRouter : public LocalSessionEventRouter {
219 virtual ~DummyRouter() {}
220 virtual void StartRoutingTo(LocalSessionEventHandler* handler) OVERRIDE {}
221 virtual void Stop() OVERRIDE {}
224 scoped_ptr<LocalSessionEventRouter> NewDummyRouter() {
225 return scoped_ptr<LocalSessionEventRouter>(new DummyRouter());
230 class SessionsSyncManagerTest
231 : public BrowserWithTestWindowTest,
232 public SessionsSyncManager::SyncInternalApiDelegate {
234 SessionsSyncManagerTest() : test_processor_(NULL) {}
236 virtual void SetUp() OVERRIDE {
237 BrowserWithTestWindowTest::SetUp();
238 browser_sync::NotificationServiceSessionsRouter* router(
239 new browser_sync::NotificationServiceSessionsRouter(
240 profile(), syncer::SyncableService::StartSyncFlare()));
241 manager_.reset(new SessionsSyncManager(profile(), this,
242 scoped_ptr<LocalSessionEventRouter>(router)));
245 virtual void TearDown() OVERRIDE {
246 test_processor_ = NULL;
249 BrowserWithTestWindowTest::TearDown();
252 virtual scoped_ptr<DeviceInfo> GetLocalDeviceInfo() const OVERRIDE {
253 return scoped_ptr<DeviceInfo>(
254 new DeviceInfo(GetLocalSyncCacheGUID(),
255 "Wayne Gretzky's Hacking Box",
258 sync_pb::SyncEnums_DeviceType_TYPE_LINUX));
261 virtual std::string GetLocalSyncCacheGUID() const OVERRIDE {
265 SessionsSyncManager* manager() { return manager_.get(); }
266 SessionSyncTestHelper* helper() { return &helper_; }
268 void InitWithSyncDataTakeOutput(const syncer::SyncDataList& initial_data,
269 syncer::SyncChangeList* output) {
270 test_processor_ = new TestSyncProcessorStub(output);
271 syncer::SyncMergeResult result = manager_->MergeDataAndStartSyncing(
272 syncer::SESSIONS, initial_data,
273 scoped_ptr<syncer::SyncChangeProcessor>(test_processor_),
274 scoped_ptr<syncer::SyncErrorFactory>(
275 new syncer::SyncErrorFactoryMock()));
276 EXPECT_FALSE(result.error().IsSet());
279 void InitWithNoSyncData() {
280 InitWithSyncDataTakeOutput(syncer::SyncDataList(), NULL);
283 void TriggerProcessSyncChangesError() {
284 test_processor_->FailProcessSyncChangesWith(syncer::SyncError(
285 FROM_HERE, syncer::SyncError::DATATYPE_ERROR, "Error",
289 void SetSyncData(const syncer::SyncDataList& data) {
290 test_processor_->SetSyncDataToReturn(data);
293 syncer::SyncChangeList* FilterOutLocalHeaderChanges(
294 syncer::SyncChangeList* list) {
295 syncer::SyncChangeList::iterator it = list->begin();
297 while (it != list->end()) {
298 if (syncer::SyncDataLocal(it->sync_data()).GetTag() ==
299 manager_->current_machine_tag()) {
300 EXPECT_TRUE(SyncChange::ACTION_ADD == it->change_type() ||
301 SyncChange::ACTION_UPDATE == it->change_type());
302 it = list->erase(it);
313 scoped_ptr<SessionsSyncManager> manager_;
314 SessionSyncTestHelper helper_;
315 TestSyncProcessorStub* test_processor_;
318 // Test that the SyncSessionManager can properly fill in a SessionHeader.
319 TEST_F(SessionsSyncManagerTest, PopulateSessionHeader) {
320 sync_pb::SessionHeader header_s;
321 header_s.set_client_name("Client 1");
322 header_s.set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_WIN);
324 SyncedSession session;
325 base::Time time = base::Time::Now();
326 SessionsSyncManager::PopulateSessionHeaderFromSpecifics(
327 header_s, time, &session);
328 ASSERT_EQ("Client 1", session.session_name);
329 ASSERT_EQ(SyncedSession::TYPE_WIN, session.device_type);
330 ASSERT_EQ(time, session.modified_time);
333 // Test translation between protobuf types and chrome session types.
334 TEST_F(SessionsSyncManagerTest, PopulateSessionWindow) {
335 sync_pb::SessionWindow window_s;
337 window_s.set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
338 window_s.set_selected_tab_index(1);
340 std::string tag = "tag";
341 SyncedSession* session = manager()->session_tracker_.GetSession(tag);
342 manager()->session_tracker_.PutWindowInSession(tag, 0);
343 manager()->BuildSyncedSessionFromSpecifics(
344 tag, window_s, base::Time(), session->windows[0]);
345 ASSERT_EQ(1U, session->windows[0]->tabs.size());
346 ASSERT_EQ(1, session->windows[0]->selected_tab_index);
347 ASSERT_EQ(1, session->windows[0]->type);
348 ASSERT_EQ(1U, manager()->session_tracker_.num_synced_sessions());
350 manager()->session_tracker_.num_synced_tabs(std::string("tag")));
355 class SyncedTabDelegateFake : public SyncedTabDelegate {
357 SyncedTabDelegateFake() : current_entry_index_(0),
358 pending_entry_index_(-1),
361 blocked_navigations_(NULL) {}
362 virtual ~SyncedTabDelegateFake() {}
364 virtual int GetCurrentEntryIndex() const OVERRIDE {
365 return current_entry_index_;
367 void set_current_entry_index(int i) {
368 current_entry_index_ = i;
371 virtual content::NavigationEntry* GetEntryAtIndex(int i) const OVERRIDE {
372 const int size = entries_.size();
373 return (size < i + 1) ? NULL : entries_[i];
376 void AppendEntry(content::NavigationEntry* entry) {
377 entries_.push_back(entry);
380 virtual int GetEntryCount() const OVERRIDE {
381 return entries_.size();
384 virtual int GetPendingEntryIndex() const OVERRIDE {
385 return pending_entry_index_;
387 void set_pending_entry_index(int i) {
388 pending_entry_index_ = i;
391 virtual SessionID::id_type GetWindowId() const OVERRIDE {
392 return SessionID::id_type();
395 virtual SessionID::id_type GetSessionId() const OVERRIDE {
396 return SessionID::id_type();
399 virtual bool IsBeingDestroyed() const OVERRIDE { return false; }
400 virtual Profile* profile() const OVERRIDE { return NULL; }
401 virtual std::string GetExtensionAppId() const OVERRIDE {
402 return std::string();
404 virtual content::NavigationEntry* GetPendingEntry() const OVERRIDE {
407 virtual content::NavigationEntry* GetActiveEntry() const OVERRIDE {
410 virtual bool ProfileIsManaged() const OVERRIDE {
413 void set_is_managed(bool is_managed) { is_managed_ = is_managed; }
414 virtual const std::vector<const content::NavigationEntry*>*
415 GetBlockedNavigations() const OVERRIDE {
416 return blocked_navigations_;
418 void set_blocked_navigations(
419 std::vector<const content::NavigationEntry*>* navs) {
420 blocked_navigations_ = navs;
422 virtual bool IsPinned() const OVERRIDE {
425 virtual bool HasWebContents() const OVERRIDE {
428 virtual content::WebContents* GetWebContents() const OVERRIDE {
432 // Session sync related methods.
433 virtual int GetSyncId() const OVERRIDE {
436 virtual void SetSyncId(int sync_id) OVERRIDE {
441 current_entry_index_ = 0;
442 pending_entry_index_ = -1;
448 int current_entry_index_;
449 int pending_entry_index_;
452 std::vector<const content::NavigationEntry*>* blocked_navigations_;
453 ScopedVector<content::NavigationEntry> entries_;
458 // Test that we exclude tabs with only chrome:// and file:// schemed navigations
459 // from ShouldSyncTab(..).
460 TEST_F(SessionsSyncManagerTest, ValidTabs) {
461 SyncedTabDelegateFake tab;
463 // A null entry shouldn't crash.
464 tab.AppendEntry(NULL);
465 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
468 // A chrome:// entry isn't valid.
469 content::NavigationEntry* entry(content::NavigationEntry::Create());
470 entry->SetVirtualURL(GURL("chrome://preferences/"));
471 tab.AppendEntry(entry);
472 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
475 // A file:// entry isn't valid, even in addition to another entry.
476 content::NavigationEntry* entry2(content::NavigationEntry::Create());
477 entry2->SetVirtualURL(GURL("file://bla"));
478 tab.AppendEntry(entry2);
479 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
481 // Add a valid scheme entry to tab, making the tab valid.
482 content::NavigationEntry* entry3(content::NavigationEntry::Create());
483 entry3->SetVirtualURL(GURL("http://www.google.com"));
484 tab.AppendEntry(entry3);
485 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
488 // Make sure GetCurrentVirtualURL() returns the virtual URL of the pending
489 // entry if the current entry is pending.
490 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLPending) {
491 SyncedTabDelegateFake tab;
492 content::NavigationEntry* entry(content::NavigationEntry::Create());
493 entry->SetVirtualURL(GURL("http://www.google.com"));
494 tab.AppendEntry(entry);
495 EXPECT_EQ(entry->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab));
498 // Make sure GetCurrentVirtualURL() returns the virtual URL of the current
499 // entry if the current entry is non-pending.
500 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLNonPending) {
501 SyncedTabDelegateFake tab;
502 content::NavigationEntry* entry(content::NavigationEntry::Create());
503 entry->SetVirtualURL(GURL("http://www.google.com"));
504 tab.AppendEntry(entry);
505 EXPECT_EQ(entry->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab));
508 static const base::Time kTime1 = base::Time::FromInternalValue(100);
509 static const base::Time kTime2 = base::Time::FromInternalValue(105);
510 static const base::Time kTime3 = base::Time::FromInternalValue(110);
511 static const base::Time kTime4 = base::Time::FromInternalValue(120);
512 static const base::Time kTime5 = base::Time::FromInternalValue(130);
514 // Populate the mock tab delegate with some data and navigation
515 // entries and make sure that setting a SessionTab from it preserves
516 // those entries (and clobbers any existing data).
517 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegate) {
518 // Create a tab with three valid entries.
519 SyncedTabDelegateFake tab;
520 content::NavigationEntry* entry1(content::NavigationEntry::Create());
521 entry1->SetVirtualURL(GURL("http://www.google.com"));
522 entry1->SetTimestamp(kTime1);
523 entry1->SetHttpStatusCode(200);
524 content::NavigationEntry* entry2(content::NavigationEntry::Create());
525 entry2->SetVirtualURL(GURL("http://www.noodle.com"));
526 entry2->SetTimestamp(kTime2);
527 entry2->SetHttpStatusCode(201);
528 content::NavigationEntry* entry3(content::NavigationEntry::Create());
529 entry3->SetVirtualURL(GURL("http://www.doodle.com"));
530 entry3->SetTimestamp(kTime3);
531 entry3->SetHttpStatusCode(202);
533 tab.AppendEntry(entry1);
534 tab.AppendEntry(entry2);
535 tab.AppendEntry(entry3);
536 tab.set_current_entry_index(2);
538 SessionTab session_tab;
539 session_tab.window_id.set_id(1);
540 session_tab.tab_id.set_id(1);
541 session_tab.tab_visual_index = 1;
542 session_tab.current_navigation_index = 1;
543 session_tab.pinned = true;
544 session_tab.extension_app_id = "app id";
545 session_tab.user_agent_override = "override";
546 session_tab.timestamp = kTime5;
547 session_tab.navigations.push_back(
548 SerializedNavigationEntryTestHelper::CreateNavigation(
549 "http://www.example.com", "Example"));
550 session_tab.session_storage_persistent_id = "persistent id";
551 manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab);
553 EXPECT_EQ(0, session_tab.window_id.id());
554 EXPECT_EQ(0, session_tab.tab_id.id());
555 EXPECT_EQ(0, session_tab.tab_visual_index);
556 EXPECT_EQ(2, session_tab.current_navigation_index);
557 EXPECT_FALSE(session_tab.pinned);
558 EXPECT_TRUE(session_tab.extension_app_id.empty());
559 EXPECT_TRUE(session_tab.user_agent_override.empty());
560 EXPECT_EQ(kTime4, session_tab.timestamp);
561 ASSERT_EQ(3u, session_tab.navigations.size());
562 EXPECT_EQ(entry1->GetVirtualURL(),
563 session_tab.navigations[0].virtual_url());
564 EXPECT_EQ(entry2->GetVirtualURL(),
565 session_tab.navigations[1].virtual_url());
566 EXPECT_EQ(entry3->GetVirtualURL(),
567 session_tab.navigations[2].virtual_url());
568 EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
569 EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
570 EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
571 EXPECT_EQ(200, session_tab.navigations[0].http_status_code());
572 EXPECT_EQ(201, session_tab.navigations[1].http_status_code());
573 EXPECT_EQ(202, session_tab.navigations[2].http_status_code());
574 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
575 session_tab.navigations[0].blocked_state());
576 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
577 session_tab.navigations[1].blocked_state());
578 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
579 session_tab.navigations[2].blocked_state());
580 EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
583 // Tests that for managed users blocked navigations are recorded and marked as
584 // such, while regular navigations are marked as allowed.
585 TEST_F(SessionsSyncManagerTest, BlockedNavigations) {
586 SyncedTabDelegateFake tab;
587 content::NavigationEntry* entry1(content::NavigationEntry::Create());
588 entry1->SetVirtualURL(GURL("http://www.google.com"));
589 entry1->SetTimestamp(kTime1);
590 tab.AppendEntry(entry1);
592 content::NavigationEntry* entry2 = content::NavigationEntry::Create();
593 entry2->SetVirtualURL(GURL("http://blocked.com/foo"));
594 entry2->SetTimestamp(kTime2);
595 content::NavigationEntry* entry3 = content::NavigationEntry::Create();
596 entry3->SetVirtualURL(GURL("http://evil.com"));
597 entry3->SetTimestamp(kTime3);
598 ScopedVector<const content::NavigationEntry> blocked_navigations;
599 blocked_navigations.push_back(entry2);
600 blocked_navigations.push_back(entry3);
602 tab.set_is_managed(true);
603 tab.set_blocked_navigations(&blocked_navigations.get());
605 SessionTab session_tab;
606 session_tab.window_id.set_id(1);
607 session_tab.tab_id.set_id(1);
608 session_tab.tab_visual_index = 1;
609 session_tab.current_navigation_index = 1;
610 session_tab.pinned = true;
611 session_tab.extension_app_id = "app id";
612 session_tab.user_agent_override = "override";
613 session_tab.timestamp = kTime5;
614 session_tab.navigations.push_back(
615 SerializedNavigationEntryTestHelper::CreateNavigation(
616 "http://www.example.com", "Example"));
617 session_tab.session_storage_persistent_id = "persistent id";
618 manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab);
620 EXPECT_EQ(0, session_tab.window_id.id());
621 EXPECT_EQ(0, session_tab.tab_id.id());
622 EXPECT_EQ(0, session_tab.tab_visual_index);
623 EXPECT_EQ(0, session_tab.current_navigation_index);
624 EXPECT_FALSE(session_tab.pinned);
625 EXPECT_TRUE(session_tab.extension_app_id.empty());
626 EXPECT_TRUE(session_tab.user_agent_override.empty());
627 EXPECT_EQ(kTime4, session_tab.timestamp);
628 ASSERT_EQ(3u, session_tab.navigations.size());
629 EXPECT_EQ(entry1->GetVirtualURL(),
630 session_tab.navigations[0].virtual_url());
631 EXPECT_EQ(entry2->GetVirtualURL(),
632 session_tab.navigations[1].virtual_url());
633 EXPECT_EQ(entry3->GetVirtualURL(),
634 session_tab.navigations[2].virtual_url());
635 EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
636 EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
637 EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
638 EXPECT_EQ(SerializedNavigationEntry::STATE_ALLOWED,
639 session_tab.navigations[0].blocked_state());
640 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
641 session_tab.navigations[1].blocked_state());
642 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
643 session_tab.navigations[2].blocked_state());
644 EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
647 // Tests that the local session header objects is created properly in
648 // presence of no other session activity, once and only once.
649 TEST_F(SessionsSyncManagerTest, MergeLocalSessionNoTabs) {
650 syncer::SyncChangeList out;
651 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
652 EXPECT_FALSE(manager()->current_machine_tag().empty());
654 EXPECT_EQ(2U, out.size());
655 EXPECT_TRUE(out[0].IsValid());
656 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
657 const SyncData data(out[0].sync_data());
658 EXPECT_EQ(manager()->current_machine_tag(),
659 syncer::SyncDataLocal(data).GetTag());
660 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
661 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
662 EXPECT_TRUE(specifics.has_header());
663 const sync_pb::SessionHeader& header_s = specifics.header();
664 EXPECT_TRUE(header_s.has_device_type());
665 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
666 EXPECT_EQ(0, header_s.window_size());
668 EXPECT_TRUE(out[1].IsValid());
669 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
670 const SyncData data_2(out[1].sync_data());
671 EXPECT_EQ(manager()->current_machine_tag(),
672 syncer::SyncDataLocal(data_2).GetTag());
673 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
674 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
675 EXPECT_TRUE(specifics2.has_header());
676 const sync_pb::SessionHeader& header_s2 = specifics2.header();
677 EXPECT_EQ(0, header_s2.window_size());
679 // Now take that header node and feed it in as input.
680 SyncData d(SyncData::CreateRemoteData(
684 syncer::AttachmentIdList(),
685 syncer::AttachmentServiceProxyForTest::Create()));
686 syncer::SyncDataList in(&d, &d + 1);
688 SessionsSyncManager manager2(profile(), this, NewDummyRouter());
689 syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
690 syncer::SESSIONS, in,
691 scoped_ptr<syncer::SyncChangeProcessor>(
692 new TestSyncProcessorStub(&out)),
693 scoped_ptr<syncer::SyncErrorFactory>(
694 new syncer::SyncErrorFactoryMock()));
695 ASSERT_FALSE(result.error().IsSet());
697 EXPECT_EQ(1U, out.size());
698 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
699 EXPECT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
702 // Ensure model association associates the pre-existing tabs.
703 TEST_F(SessionsSyncManagerTest, SwappedOutOnRestore) {
704 AddTab(browser(), GURL("http://foo1"));
705 NavigateAndCommitActiveTab(GURL("http://foo2"));
706 AddTab(browser(), GURL("http://bar1"));
707 NavigateAndCommitActiveTab(GURL("http://bar2"));
708 AddTab(browser(), GURL("http://baz1"));
709 NavigateAndCommitActiveTab(GURL("http://baz2"));
710 const int kRestoredTabId = 1337;
711 const int kNewTabId = 2468;
713 syncer::SyncDataList in;
714 syncer::SyncChangeList out;
715 InitWithSyncDataTakeOutput(in, &out);
717 // Should be one header add, 3 tab add/update pairs, one header update.
718 ASSERT_EQ(8U, out.size());
720 // For input, we set up:
721 // * one "normal" fully loaded tab
722 // * one "frozen" tab with no WebContents and a tab_id change
723 // * one "frozen" tab with no WebContents and no tab_id change
724 SyncData t0(SyncData::CreateRemoteData(
726 out[2].sync_data().GetSpecifics(),
728 syncer::AttachmentIdList(),
729 syncer::AttachmentServiceProxyForTest::Create()));
730 sync_pb::EntitySpecifics entity(out[4].sync_data().GetSpecifics());
731 entity.mutable_session()->mutable_tab()->set_tab_id(kRestoredTabId);
732 SyncData t1(SyncData::CreateRemoteData(
736 syncer::AttachmentIdList(),
737 syncer::AttachmentServiceProxyForTest::Create()));
738 SyncData t2(SyncData::CreateRemoteData(
740 out[6].sync_data().GetSpecifics(),
742 syncer::AttachmentIdList(),
743 syncer::AttachmentServiceProxyForTest::Create()));
748 manager()->StopSyncing(syncer::SESSIONS);
750 const std::set<SyncedWindowDelegate*> windows(
751 SyncedWindowDelegate::GetSyncedWindowDelegates());
752 ASSERT_EQ(1U, windows.size());
753 SyncedTabDelegateFake t1_override, t2_override;
754 t1_override.SetSyncId(1); // No WebContents by default.
755 t2_override.SetSyncId(2); // No WebContents by default.
756 SyncedWindowDelegateOverride window_override(*windows.begin());
757 window_override.OverrideTabAt(1, &t1_override, kNewTabId);
758 window_override.OverrideTabAt(2, &t2_override,
759 t2.GetSpecifics().session().tab().tab_id());
760 std::set<SyncedWindowDelegate*> delegates;
761 delegates.insert(&window_override);
762 scoped_ptr<TestSyncedWindowDelegatesGetter> getter(
763 new TestSyncedWindowDelegatesGetter(delegates));
764 manager()->synced_window_getter_.reset(getter.release());
766 syncer::SyncMergeResult result = manager()->MergeDataAndStartSyncing(
767 syncer::SESSIONS, in,
768 scoped_ptr<syncer::SyncChangeProcessor>(
769 new TestSyncProcessorStub(&out)),
770 scoped_ptr<syncer::SyncErrorFactory>(
771 new syncer::SyncErrorFactoryMock()));
773 // There should be two changes, one for the fully associated tab, and
774 // one for the tab_id update to t1. t2 shouldn't need to be updated.
775 ASSERT_EQ(2U, FilterOutLocalHeaderChanges(&out)->size());
776 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
777 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
779 out[1].sync_data().GetSpecifics().session().tab().tab_id());
782 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
783 ASSERT_EQ(3U, tab_map.size());
784 int t2_tab_id = t2.GetSpecifics().session().tab().tab_id();
785 EXPECT_EQ(2, tab_map.find(t2_tab_id)->second->tab_node_id());
786 EXPECT_EQ(1, tab_map.find(kNewTabId)->second->tab_node_id());
787 int t0_tab_id = out[0].sync_data().GetSpecifics().session().tab().tab_id();
788 EXPECT_EQ(0, tab_map.find(t0_tab_id)->second->tab_node_id());
789 // TODO(tim): Once bug 337057 is fixed, we can issue an OnLocalTabModified
790 // from here (using an override similar to above) to return a new tab id
791 // and verify that we don't see any node creations in the SyncChangeProcessor
792 // (similar to how SessionsSyncManagerTest.OnLocalTabModified works.)
795 // Tests MergeDataAndStartSyncing with sync data but no local data.
796 TEST_F(SessionsSyncManagerTest, MergeWithInitialForeignSession) {
797 std::string tag = "tag1";
799 SessionID::id_type n1[] = {5, 10, 13, 17};
800 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
801 std::vector<sync_pb::SessionSpecifics> tabs1;
802 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
803 tag, tab_list1, &tabs1));
804 // Add a second window.
805 SessionID::id_type n2[] = {7, 15, 18, 20};
806 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
807 helper()->AddWindowSpecifics(1, tab_list2, &meta);
809 // Set up initial data.
810 syncer::SyncDataList initial_data;
811 sync_pb::EntitySpecifics entity;
812 entity.mutable_session()->CopyFrom(meta);
813 initial_data.push_back(SyncData::CreateRemoteData(
817 syncer::AttachmentIdList(),
818 syncer::AttachmentServiceProxyForTest::Create()));
819 AddTabsToSyncDataList(tabs1, &initial_data);
821 for (size_t i = 0; i < tab_list2.size(); ++i) {
822 sync_pb::EntitySpecifics entity;
823 helper()->BuildTabSpecifics(tag, 0, tab_list2[i],
824 entity.mutable_session());
825 initial_data.push_back(SyncData::CreateRemoteData(
829 syncer::AttachmentIdList(),
830 syncer::AttachmentServiceProxyForTest::Create()));
833 syncer::SyncChangeList output;
834 InitWithSyncDataTakeOutput(initial_data, &output);
835 EXPECT_TRUE(FilterOutLocalHeaderChanges(&output)->empty());
837 std::vector<const SyncedSession*> foreign_sessions;
838 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
839 ASSERT_EQ(1U, foreign_sessions.size());
840 std::vector<std::vector<SessionID::id_type> > session_reference;
841 session_reference.push_back(tab_list1);
842 session_reference.push_back(tab_list2);
843 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
846 // This is a combination of MergeWithInitialForeignSession and
847 // MergeLocalSessionExistingTabs. We repeat some checks performed in each of
848 // those tests to ensure the common mixed scenario works.
849 TEST_F(SessionsSyncManagerTest, MergeWithLocalAndForeignTabs) {
851 AddTab(browser(), GURL("http://foo1"));
852 NavigateAndCommitActiveTab(GURL("http://foo2"));
855 std::string tag = "tag1";
856 SessionID::id_type n1[] = {5, 10, 13, 17};
857 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
858 std::vector<sync_pb::SessionSpecifics> tabs1;
859 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
860 tag, tab_list1, &tabs1));
861 syncer::SyncDataList foreign_data;
862 sync_pb::EntitySpecifics entity;
863 entity.mutable_session()->CopyFrom(meta);
864 foreign_data.push_back(SyncData::CreateRemoteData(
868 syncer::AttachmentIdList(),
869 syncer::AttachmentServiceProxyForTest::Create()));
870 AddTabsToSyncDataList(tabs1, &foreign_data);
872 syncer::SyncChangeList output;
873 InitWithSyncDataTakeOutput(foreign_data, &output);
874 ASSERT_EQ(4U, output.size());
876 // Verify the local header.
877 EXPECT_TRUE(output[0].IsValid());
878 EXPECT_EQ(SyncChange::ACTION_ADD, output[0].change_type());
879 const SyncData data(output[0].sync_data());
880 EXPECT_EQ(manager()->current_machine_tag(),
881 syncer::SyncDataLocal(data).GetTag());
882 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
883 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
884 EXPECT_TRUE(specifics.has_header());
885 const sync_pb::SessionHeader& header_s = specifics.header();
886 EXPECT_TRUE(header_s.has_device_type());
887 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
888 EXPECT_EQ(0, header_s.window_size());
890 // Verify the tab node creations and updates with content.
891 for (int i = 1; i < 3; i++) {
892 EXPECT_TRUE(output[i].IsValid());
893 const SyncData data(output[i].sync_data());
894 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(),
895 manager()->current_machine_tag(), true));
896 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
897 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
899 EXPECT_EQ(SyncChange::ACTION_ADD, output[1].change_type());
900 EXPECT_EQ(SyncChange::ACTION_UPDATE, output[2].change_type());
901 EXPECT_TRUE(output[2].sync_data().GetSpecifics().session().has_tab());
903 // Verify the header was updated to reflect window state.
904 EXPECT_TRUE(output[3].IsValid());
905 EXPECT_EQ(SyncChange::ACTION_UPDATE, output[3].change_type());
906 const SyncData data_2(output[3].sync_data());
907 EXPECT_EQ(manager()->current_machine_tag(),
908 syncer::SyncDataLocal(data_2).GetTag());
909 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
910 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
911 EXPECT_TRUE(specifics2.has_header());
912 const sync_pb::SessionHeader& header_s2 = specifics2.header();
913 EXPECT_EQ(1, header_s2.window_size());
915 // Verify foreign data.
916 std::vector<const SyncedSession*> foreign_sessions;
917 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
918 std::vector<std::vector<SessionID::id_type> > session_reference;
919 session_reference.push_back(tab_list1);
920 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
921 // There should be one and only one foreign session. If VerifySyncedSession
922 // was successful above this EXPECT call ensures the local session didn't
923 // get mistakenly added to foreign tracking (Similar to ExistingTabs test).
924 EXPECT_EQ(1U, foreign_sessions.size());
927 // Tests the common scenario. Merge with both local and foreign session data
928 // followed by updates flowing from sync and local.
929 TEST_F(SessionsSyncManagerTest, UpdatesAfterMixedMerge) {
930 // Add local and foreign data.
931 AddTab(browser(), GURL("http://foo1"));
932 NavigateAndCommitActiveTab(GURL("http://foo2"));
934 std::string tag1 = "tag1";
935 syncer::SyncDataList foreign_data1;
936 std::vector<std::vector<SessionID::id_type> > meta1_reference;
937 sync_pb::SessionSpecifics meta1;
939 SessionID::id_type n1[] = {5, 10, 13, 17};
940 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
941 meta1_reference.push_back(tab_list1);
942 std::vector<sync_pb::SessionSpecifics> tabs1;
943 meta1 = helper()->BuildForeignSession(tag1, tab_list1, &tabs1);
944 sync_pb::EntitySpecifics entity;
945 entity.mutable_session()->CopyFrom(meta1);
946 foreign_data1.push_back(SyncData::CreateRemoteData(
950 syncer::AttachmentIdList(),
951 syncer::AttachmentServiceProxyForTest::Create()));
952 AddTabsToSyncDataList(tabs1, &foreign_data1);
954 syncer::SyncChangeList output1;
955 InitWithSyncDataTakeOutput(foreign_data1, &output1);
956 ASSERT_EQ(4U, output1.size());
958 // Add a second window to the foreign session.
959 // TODO(tim): Bug 98892. Add local window too when observers are hooked up.
960 SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
961 std::vector<SessionID::id_type> tab_list2(
962 tab_nums2, tab_nums2 + arraysize(tab_nums2));
963 meta1_reference.push_back(tab_list2);
964 helper()->AddWindowSpecifics(1, tab_list2, &meta1);
965 std::vector<sync_pb::SessionSpecifics> tabs2;
966 tabs2.resize(tab_list2.size());
967 for (size_t i = 0; i < tab_list2.size(); ++i) {
968 helper()->BuildTabSpecifics(tag1, 0, tab_list2[i], &tabs2[i]);
971 syncer::SyncChangeList changes;
972 changes.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE));
973 AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes);
974 manager()->ProcessSyncChanges(FROM_HERE, changes);
977 // Check that the foreign session was associated and retrieve the data.
978 std::vector<const SyncedSession*> foreign_sessions;
979 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
980 ASSERT_EQ(1U, foreign_sessions.size());
981 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
982 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
983 helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0]));
985 // Add a new foreign session.
986 std::string tag2 = "tag2";
987 SessionID::id_type n2[] = {107, 115};
988 std::vector<SessionID::id_type> tag2_tab_list(n2, n2 + arraysize(n2));
989 std::vector<sync_pb::SessionSpecifics> tag2_tabs;
990 sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession(
991 tag2, tag2_tab_list, &tag2_tabs));
992 changes.push_back(MakeRemoteChange(100, meta2, SyncChange::ACTION_ADD));
993 AddTabsToChangeList(tag2_tabs, SyncChange::ACTION_ADD, &changes);
995 manager()->ProcessSyncChanges(FROM_HERE, changes);
998 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
999 std::vector<std::vector<SessionID::id_type> > meta2_reference;
1000 meta2_reference.push_back(tag2_tab_list);
1001 ASSERT_EQ(2U, foreign_sessions.size());
1002 ASSERT_EQ(2U, foreign_sessions[1]->windows.find(0)->second->tabs.size());
1003 helper()->VerifySyncedSession(tag2, meta2_reference, *(foreign_sessions[1]));
1004 foreign_sessions.clear();
1006 // Remove a tab from a window.
1007 meta1_reference[0].pop_back();
1008 tab_list1.pop_back();
1009 sync_pb::SessionWindow* win = meta1.mutable_header()->mutable_window(0);
1011 for (std::vector<int>::const_iterator iter = tab_list1.begin();
1012 iter != tab_list1.end(); ++iter) {
1013 win->add_tab(*iter);
1015 syncer::SyncChangeList removal;
1016 removal.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE));
1017 AddTabsToChangeList(tabs1, SyncChange::ACTION_UPDATE, &removal);
1018 manager()->ProcessSyncChanges(FROM_HERE, removal);
1020 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1021 ASSERT_EQ(2U, foreign_sessions.size());
1022 ASSERT_EQ(3U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
1023 helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0]));
1026 // Tests that this SyncSessionManager knows how to delete foreign sessions
1028 TEST_F(SessionsSyncManagerTest, DeleteForeignSession) {
1029 InitWithNoSyncData();
1030 std::string tag = "tag1";
1031 syncer::SyncChangeList changes;
1033 std::vector<const SyncedSession*> foreign_sessions;
1034 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1035 manager()->DeleteForeignSessionInternal(tag, &changes);
1036 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1037 EXPECT_TRUE(changes.empty());
1039 // Fill an instance of session specifics with a foreign session's data.
1040 std::vector<sync_pb::SessionSpecifics> tabs;
1041 SessionID::id_type n1[] = {5, 10, 13, 17};
1042 std::vector<SessionID::id_type> tab_nums1(n1, n1 + arraysize(n1));
1043 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1044 tag, tab_nums1, &tabs));
1046 // Update associator with the session's meta node, window, and tabs.
1047 manager()->UpdateTrackerWithForeignSession(meta, base::Time());
1048 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs.begin();
1049 iter != tabs.end(); ++iter) {
1050 manager()->UpdateTrackerWithForeignSession(*iter, base::Time());
1052 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1053 ASSERT_EQ(1U, foreign_sessions.size());
1055 // Now delete the foreign session.
1056 manager()->DeleteForeignSessionInternal(tag, &changes);
1057 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1059 EXPECT_EQ(5U, changes.size());
1060 std::set<std::string> expected_tags(&tag, &tag + 1);
1061 for (int i = 0; i < 5; i++)
1062 expected_tags.insert(TabNodePool::TabIdToTag(tag, i));
1064 for (int i = 0; i < 5; i++) {
1065 SCOPED_TRACE(changes[i].ToString());
1066 EXPECT_TRUE(changes[i].IsValid());
1067 EXPECT_EQ(SyncChange::ACTION_DELETE, changes[i].change_type());
1068 EXPECT_TRUE(changes[i].sync_data().IsValid());
1070 expected_tags.erase(
1071 syncer::SyncDataLocal(changes[i].sync_data()).GetTag()));
1075 // Write a foreign session to a node, with the tabs arriving first, and then
1077 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeTabsFirst) {
1078 InitWithNoSyncData();
1080 // Fill an instance of session specifics with a foreign session's data.
1081 std::string tag = "tag1";
1082 SessionID::id_type nums1[] = {5, 10, 13, 17};
1083 std::vector<sync_pb::SessionSpecifics> tabs1;
1084 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
1085 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1086 tag, tab_list1, &tabs1));
1088 syncer::SyncChangeList adds;
1089 // Add tabs for first window, then the meta node.
1090 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &adds);
1091 adds.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1092 manager()->ProcessSyncChanges(FROM_HERE, adds);
1094 // Check that the foreign session was associated and retrieve the data.
1095 std::vector<const SyncedSession*> foreign_sessions;
1096 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1097 ASSERT_EQ(1U, foreign_sessions.size());
1098 std::vector<std::vector<SessionID::id_type> > session_reference;
1099 session_reference.push_back(tab_list1);
1100 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
1103 // Write a foreign session to a node with some tabs that never arrive.
1104 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeMissingTabs) {
1105 InitWithNoSyncData();
1107 // Fill an instance of session specifics with a foreign session's data.
1108 std::string tag = "tag1";
1109 SessionID::id_type nums1[] = {5, 10, 13, 17};
1110 std::vector<sync_pb::SessionSpecifics> tabs1;
1111 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
1112 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1113 tag, tab_list1, &tabs1));
1114 // Add a second window, but this time only create two tab nodes, despite the
1115 // window expecting four tabs.
1116 SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
1117 std::vector<SessionID::id_type> tab_list2(
1118 tab_nums2, tab_nums2 + arraysize(tab_nums2));
1119 helper()->AddWindowSpecifics(1, tab_list2, &meta);
1120 std::vector<sync_pb::SessionSpecifics> tabs2;
1122 for (size_t i = 0; i < 2; ++i) {
1123 helper()->BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
1126 syncer::SyncChangeList changes;
1127 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1128 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1129 AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes);
1130 manager()->ProcessSyncChanges(FROM_HERE, changes);
1133 // Check that the foreign session was associated and retrieve the data.
1134 std::vector<const SyncedSession*> foreign_sessions;
1135 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1136 ASSERT_EQ(1U, foreign_sessions.size());
1137 ASSERT_EQ(2U, foreign_sessions[0]->windows.size());
1138 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
1139 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
1141 // Close the second window.
1142 meta.mutable_header()->clear_window();
1143 helper()->AddWindowSpecifics(0, tab_list1, &meta);
1144 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_UPDATE));
1145 // Update associator with the session's meta node containing one window.
1146 manager()->ProcessSyncChanges(FROM_HERE, changes);
1148 // Check that the foreign session was associated and retrieve the data.
1149 foreign_sessions.clear();
1150 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1151 ASSERT_EQ(1U, foreign_sessions.size());
1152 ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
1153 std::vector<std::vector<SessionID::id_type> > session_reference;
1154 session_reference.push_back(tab_list1);
1155 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
1158 // Tests that the SessionsSyncManager can handle a remote client deleting
1159 // sync nodes that belong to this local session.
1160 TEST_F(SessionsSyncManagerTest, ProcessRemoteDeleteOfLocalSession) {
1161 syncer::SyncChangeList out;
1162 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1163 ASSERT_EQ(2U, out.size());
1164 sync_pb::EntitySpecifics entity(out[0].sync_data().GetSpecifics());
1165 SyncData d(SyncData::CreateRemoteData(
1169 syncer::AttachmentIdList(),
1170 syncer::AttachmentServiceProxyForTest::Create()));
1171 SetSyncData(syncer::SyncDataList(&d, &d + 1));
1174 syncer::SyncChangeList changes;
1176 MakeRemoteChange(1, entity.session(), SyncChange::ACTION_DELETE));
1177 manager()->ProcessSyncChanges(FROM_HERE, changes);
1178 EXPECT_TRUE(manager()->local_tab_pool_out_of_sync_);
1179 EXPECT_TRUE(out.empty()); // ChangeProcessor shouldn't see any activity.
1181 // This should trigger repair of the TabNodePool.
1182 const GURL foo1("http://foo/1");
1183 AddTab(browser(), foo1);
1184 EXPECT_FALSE(manager()->local_tab_pool_out_of_sync_);
1186 // AddTab triggers two notifications, one for the tab insertion and one for
1187 // committing the NavigationEntry. The first notification results in a tab
1188 // we don't associate although we do update the header node. The second
1189 // notification triggers association of the tab, and the subsequent window
1190 // update. So we should see 4 changes at the SyncChangeProcessor.
1191 ASSERT_EQ(4U, out.size());
1193 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
1194 ASSERT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
1195 EXPECT_EQ(SyncChange::ACTION_ADD, out[1].change_type());
1196 int tab_node_id = out[1].sync_data().GetSpecifics().session().tab_node_id();
1197 EXPECT_EQ(TabNodePool::TabIdToTag(
1198 manager()->current_machine_tag(), tab_node_id),
1199 syncer::SyncDataLocal(out[1].sync_data()).GetTag());
1200 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
1201 ASSERT_TRUE(out[2].sync_data().GetSpecifics().session().has_tab());
1202 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[3].change_type());
1203 ASSERT_TRUE(out[3].sync_data().GetSpecifics().session().has_header());
1205 // Verify the actual content.
1206 const sync_pb::SessionHeader& session_header =
1207 out[3].sync_data().GetSpecifics().session().header();
1208 ASSERT_EQ(1, session_header.window_size());
1209 EXPECT_EQ(1, session_header.window(0).tab_size());
1210 const sync_pb::SessionTab& tab1 =
1211 out[2].sync_data().GetSpecifics().session().tab();
1212 ASSERT_EQ(1, tab1.navigation_size());
1213 EXPECT_EQ(foo1.spec(), tab1.navigation(0).virtual_url());
1215 // Verify TabNodePool integrity.
1216 EXPECT_EQ(1U, manager()->local_tab_pool_.Capacity());
1217 EXPECT_TRUE(manager()->local_tab_pool_.Empty());
1220 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
1221 ASSERT_EQ(1U, tab_map.size());
1222 int tab_id = out[2].sync_data().GetSpecifics().session().tab().tab_id();
1223 EXPECT_EQ(tab_node_id, tab_map.find(tab_id)->second->tab_node_id());
1226 // Test that receiving a session delete from sync removes the session
1228 TEST_F(SessionsSyncManagerTest, ProcessForeignDelete) {
1229 InitWithNoSyncData();
1230 SessionID::id_type n[] = {5};
1231 std::vector<sync_pb::SessionSpecifics> tabs1;
1232 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
1233 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1234 "tag1", tab_list, &tabs1));
1236 syncer::SyncChangeList changes;
1237 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1238 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1239 manager()->ProcessSyncChanges(FROM_HERE, changes);
1241 std::vector<const SyncedSession*> foreign_sessions;
1242 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1243 ASSERT_EQ(1U, foreign_sessions.size());
1246 foreign_sessions.clear();
1247 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE));
1248 manager()->ProcessSyncChanges(FROM_HERE, changes);
1250 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1253 // TODO(shashishekhar): "Move this to TabNodePool unittests."
1254 TEST_F(SessionsSyncManagerTest, SaveUnassociatedNodesForReassociation) {
1255 syncer::SyncChangeList changes;
1256 InitWithNoSyncData();
1258 std::string local_tag = manager()->current_machine_tag();
1259 // Create a free node and then dissassociate sessions so that it ends up
1261 manager()->local_tab_pool_.GetFreeTabNode(&changes);
1263 // Update the tab_id of the node, so that it is considered a valid
1264 // unassociated node otherwise it will be mistaken for a corrupted node and
1265 // will be deleted before being added to the tab node pool.
1266 sync_pb::EntitySpecifics entity(changes[0].sync_data().GetSpecifics());
1267 entity.mutable_session()->mutable_tab()->set_tab_id(1);
1268 SyncData d(SyncData::CreateRemoteData(
1272 syncer::AttachmentIdList(),
1273 syncer::AttachmentServiceProxyForTest::Create()));
1274 syncer::SyncDataList in(&d, &d + 1);
1276 SessionsSyncManager manager2(profile(), this, NewDummyRouter());
1277 syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
1278 syncer::SESSIONS, in,
1279 scoped_ptr<syncer::SyncChangeProcessor>(
1280 new TestSyncProcessorStub(&changes)),
1281 scoped_ptr<syncer::SyncErrorFactory>(
1282 new syncer::SyncErrorFactoryMock()));
1283 ASSERT_FALSE(result.error().IsSet());
1284 EXPECT_TRUE(FilterOutLocalHeaderChanges(&changes)->empty());
1287 TEST_F(SessionsSyncManagerTest, MergeDeletesCorruptNode) {
1288 syncer::SyncChangeList changes;
1289 InitWithNoSyncData();
1291 std::string local_tag = manager()->current_machine_tag();
1292 int tab_node_id = manager()->local_tab_pool_.GetFreeTabNode(&changes);
1293 SyncData d(SyncData::CreateRemoteData(
1295 changes[0].sync_data().GetSpecifics(),
1297 syncer::AttachmentIdList(),
1298 syncer::AttachmentServiceProxyForTest::Create()));
1299 syncer::SyncDataList in(&d, &d + 1);
1303 InitWithSyncDataTakeOutput(in, &changes);
1304 EXPECT_EQ(1U, FilterOutLocalHeaderChanges(&changes)->size());
1305 EXPECT_EQ(SyncChange::ACTION_DELETE, changes[0].change_type());
1306 EXPECT_EQ(TabNodePool::TabIdToTag(local_tag, tab_node_id),
1307 syncer::SyncDataLocal(changes[0].sync_data()).GetTag());
1310 // Test that things work if a tab is initially ignored.
1311 TEST_F(SessionsSyncManagerTest, AssociateWindowsDontReloadTabs) {
1312 syncer::SyncChangeList out;
1313 // Go to a URL that is ignored by session syncing.
1314 AddTab(browser(), GURL("chrome://preferences/"));
1315 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1316 ASSERT_EQ(2U, out.size()); // Header add and update.
1319 out[1].sync_data().GetSpecifics().session().header().window_size());
1322 // Go to a sync-interesting URL.
1323 NavigateAndCommitActiveTab(GURL("http://foo2"));
1325 EXPECT_EQ(3U, out.size()); // Tab add, update, and header update.
1328 StartsWithASCII(syncer::SyncDataLocal(out[0].sync_data()).GetTag(),
1329 manager()->current_machine_tag(),
1331 EXPECT_EQ(manager()->current_machine_tag(),
1332 out[0].sync_data().GetSpecifics().session().session_tag());
1333 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
1336 StartsWithASCII(syncer::SyncDataLocal(out[1].sync_data()).GetTag(),
1337 manager()->current_machine_tag(),
1339 EXPECT_EQ(manager()->current_machine_tag(),
1340 out[1].sync_data().GetSpecifics().session().session_tag());
1341 EXPECT_TRUE(out[1].sync_data().GetSpecifics().session().has_tab());
1342 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
1344 EXPECT_TRUE(out[2].IsValid());
1345 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
1346 const SyncData data(out[2].sync_data());
1347 EXPECT_EQ(manager()->current_machine_tag(),
1348 syncer::SyncDataLocal(data).GetTag());
1349 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1350 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1351 EXPECT_TRUE(specifics.has_header());
1352 const sync_pb::SessionHeader& header_s = specifics.header();
1353 EXPECT_EQ(1, header_s.window_size());
1354 EXPECT_EQ(1, header_s.window(0).tab_size());
1357 // Tests that the SyncSessionManager responds to local tab events properly.
1358 TEST_F(SessionsSyncManagerTest, OnLocalTabModified) {
1359 syncer::SyncChangeList out;
1360 // Init with no local data, relies on MergeLocalSessionNoTabs.
1361 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1362 ASSERT_FALSE(manager()->current_machine_tag().empty());
1363 ASSERT_EQ(2U, out.size());
1365 // Copy the original header.
1366 sync_pb::EntitySpecifics header(out[0].sync_data().GetSpecifics());
1369 const GURL foo1("http://foo/1");
1370 const GURL foo2("http://foo/2");
1371 const GURL bar1("http://bar/1");
1372 const GURL bar2("http://bar/2");
1373 AddTab(browser(), foo1);
1374 NavigateAndCommitActiveTab(foo2);
1375 AddTab(browser(), bar1);
1376 NavigateAndCommitActiveTab(bar2);
1378 // One add, one update for each AddTab.
1379 // One update for each NavigateAndCommit.
1380 // = 6 total tab updates.
1381 // One header update corresponding to each of those.
1382 // = 6 total header updates.
1383 // 12 total updates.
1384 ASSERT_EQ(12U, out.size());
1386 // Verify the tab node creations and updates to ensure the SyncProcessor
1387 // sees the right operations.
1388 for (int i = 0; i < 12; i++) {
1390 EXPECT_TRUE(out[i].IsValid());
1391 const SyncData data(out[i].sync_data());
1392 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(),
1393 manager()->current_machine_tag(), true));
1394 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1395 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1397 // First thing on an AddTab is a no-op header update for parented tab.
1398 EXPECT_EQ(header.SerializeAsString(),
1399 data.GetSpecifics().SerializeAsString());
1400 EXPECT_EQ(manager()->current_machine_tag(),
1401 syncer::SyncDataLocal(data).GetTag());
1402 } else if (i % 6 == 1) {
1403 // Next, the TabNodePool should create the tab node.
1404 EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
1405 EXPECT_EQ(TabNodePool::TabIdToTag(
1406 manager()->current_machine_tag(),
1407 data.GetSpecifics().session().tab_node_id()),
1408 syncer::SyncDataLocal(data).GetTag());
1409 } else if (i % 6 == 2) {
1410 // Then we see the tab update to the URL.
1411 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1412 EXPECT_EQ(TabNodePool::TabIdToTag(
1413 manager()->current_machine_tag(),
1414 data.GetSpecifics().session().tab_node_id()),
1415 syncer::SyncDataLocal(data).GetTag());
1416 ASSERT_TRUE(specifics.has_tab());
1417 } else if (i % 6 == 3) {
1418 // The header needs to be updated to reflect the new window state.
1419 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1420 EXPECT_TRUE(specifics.has_header());
1421 } else if (i % 6 == 4) {
1422 // Now we move on to NavigateAndCommit. Update the tab.
1423 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1424 EXPECT_EQ(TabNodePool::TabIdToTag(
1425 manager()->current_machine_tag(),
1426 data.GetSpecifics().session().tab_node_id()),
1427 syncer::SyncDataLocal(data).GetTag());
1428 ASSERT_TRUE(specifics.has_tab());
1429 } else if (i % 6 == 5) {
1430 // The header needs to be updated to reflect the new window state.
1431 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1432 ASSERT_TRUE(specifics.has_header());
1433 header = data.GetSpecifics();
1437 // Verify the actual content to ensure sync sees the right data.
1438 // When it's all said and done, the header should reflect two tabs.
1439 const sync_pb::SessionHeader& session_header = header.session().header();
1440 ASSERT_EQ(1, session_header.window_size());
1441 EXPECT_EQ(2, session_header.window(0).tab_size());
1443 // ASSERT_TRUEs above allow us to dive in freely here.
1444 // Verify first tab.
1445 const sync_pb::SessionTab& tab1_1 =
1446 out[2].sync_data().GetSpecifics().session().tab();
1447 ASSERT_EQ(1, tab1_1.navigation_size());
1448 EXPECT_EQ(foo1.spec(), tab1_1.navigation(0).virtual_url());
1449 const sync_pb::SessionTab& tab1_2 =
1450 out[4].sync_data().GetSpecifics().session().tab();
1451 ASSERT_EQ(2, tab1_2.navigation_size());
1452 EXPECT_EQ(foo1.spec(), tab1_2.navigation(0).virtual_url());
1453 EXPECT_EQ(foo2.spec(), tab1_2.navigation(1).virtual_url());
1455 // Verify second tab.
1456 const sync_pb::SessionTab& tab2_1 =
1457 out[8].sync_data().GetSpecifics().session().tab();
1458 ASSERT_EQ(1, tab2_1.navigation_size());
1459 EXPECT_EQ(bar1.spec(), tab2_1.navigation(0).virtual_url());
1460 const sync_pb::SessionTab& tab2_2 =
1461 out[10].sync_data().GetSpecifics().session().tab();
1462 ASSERT_EQ(2, tab2_2.navigation_size());
1463 EXPECT_EQ(bar1.spec(), tab2_2.navigation(0).virtual_url());
1464 EXPECT_EQ(bar2.spec(), tab2_2.navigation(1).virtual_url());
1467 // Ensure model association associates the pre-existing tabs.
1468 TEST_F(SessionsSyncManagerTest, MergeLocalSessionExistingTabs) {
1469 AddTab(browser(), GURL("http://foo1"));
1470 NavigateAndCommitActiveTab(GURL("http://foo2")); // Adds back entry.
1471 AddTab(browser(), GURL("http://bar1"));
1472 NavigateAndCommitActiveTab(GURL("http://bar2")); // Adds back entry.
1474 syncer::SyncChangeList out;
1475 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1476 ASSERT_EQ(6U, out.size());
1478 // Check that this machine's data is not included in the foreign windows.
1479 std::vector<const SyncedSession*> foreign_sessions;
1480 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1482 // Verify the header.
1483 EXPECT_TRUE(out[0].IsValid());
1484 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
1485 const SyncData data(out[0].sync_data());
1486 EXPECT_EQ(manager()->current_machine_tag(),
1487 syncer::SyncDataLocal(data).GetTag());
1488 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1489 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1490 EXPECT_TRUE(specifics.has_header());
1491 const sync_pb::SessionHeader& header_s = specifics.header();
1492 EXPECT_TRUE(header_s.has_device_type());
1493 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
1494 EXPECT_EQ(0, header_s.window_size());
1496 // Verify the tab node creations and updates with content.
1497 for (int i = 1; i < 5; i++) {
1498 EXPECT_TRUE(out[i].IsValid());
1499 const SyncData data(out[i].sync_data());
1500 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(),
1501 manager()->current_machine_tag(), true));
1502 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1503 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1505 EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
1507 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1508 EXPECT_TRUE(specifics.has_tab());
1512 // Verify the header was updated to reflect new window state.
1513 EXPECT_TRUE(out[5].IsValid());
1514 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type());
1515 const SyncData data_2(out[5].sync_data());
1516 EXPECT_EQ(manager()->current_machine_tag(),
1517 syncer::SyncDataLocal(data_2).GetTag());
1518 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
1519 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
1520 EXPECT_TRUE(specifics2.has_header());
1521 const sync_pb::SessionHeader& header_s2 = specifics2.header();
1522 EXPECT_EQ(1, header_s2.window_size());
1525 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
1526 ASSERT_EQ(2U, tab_map.size());
1527 // Tabs are ordered by sessionid in tab_map, so should be able to traverse
1528 // the tree based on order of tabs created
1529 SessionsSyncManager::TabLinksMap::iterator iter = tab_map.begin();
1530 ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
1531 EXPECT_EQ(GURL("http://foo1"), iter->second->tab()->
1532 GetEntryAtIndex(0)->GetVirtualURL());
1533 EXPECT_EQ(GURL("http://foo2"), iter->second->tab()->
1534 GetEntryAtIndex(1)->GetVirtualURL());
1536 ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
1537 EXPECT_EQ(GURL("http://bar1"), iter->second->tab()->
1538 GetEntryAtIndex(0)->GetVirtualURL());
1539 EXPECT_EQ(GURL("http://bar2"), iter->second->tab()->
1540 GetEntryAtIndex(1)->GetVirtualURL());
1543 // Test garbage collection of stale foreign sessions.
1544 TEST_F(SessionsSyncManagerTest, DoGarbageCollection) {
1545 // Fill two instances of session specifics with a foreign session's data.
1546 std::string tag1 = "tag1";
1547 SessionID::id_type n1[] = {5, 10, 13, 17};
1548 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
1549 std::vector<sync_pb::SessionSpecifics> tabs1;
1550 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1551 tag1, tab_list1, &tabs1));
1552 std::string tag2 = "tag2";
1553 SessionID::id_type n2[] = {8, 15, 18, 20};
1554 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
1555 std::vector<sync_pb::SessionSpecifics> tabs2;
1556 sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession(
1557 tag2, tab_list2, &tabs2));
1558 // Set the modification time for tag1 to be 21 days ago, tag2 to 5 days ago.
1559 base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21);
1560 base::Time tag2_time = base::Time::Now() - base::TimeDelta::FromDays(5);
1562 syncer::SyncDataList foreign_data;
1563 sync_pb::EntitySpecifics entity1, entity2;
1564 entity1.mutable_session()->CopyFrom(meta);
1565 entity2.mutable_session()->CopyFrom(meta2);
1566 foreign_data.push_back(SyncData::CreateRemoteData(
1570 syncer::AttachmentIdList(),
1571 syncer::AttachmentServiceProxyForTest::Create()));
1572 foreign_data.push_back(SyncData::CreateRemoteData(
1576 syncer::AttachmentIdList(),
1577 syncer::AttachmentServiceProxyForTest::Create()));
1578 AddTabsToSyncDataList(tabs1, &foreign_data);
1579 AddTabsToSyncDataList(tabs2, &foreign_data);
1581 syncer::SyncChangeList output;
1582 InitWithSyncDataTakeOutput(foreign_data, &output);
1583 ASSERT_EQ(2U, output.size());
1586 // Check that the foreign session was associated and retrieve the data.
1587 std::vector<const SyncedSession*> foreign_sessions;
1588 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1589 ASSERT_EQ(2U, foreign_sessions.size());
1590 foreign_sessions.clear();
1592 // Now garbage collect and verify the non-stale session is still there.
1593 manager()->DoGarbageCollection();
1594 ASSERT_EQ(5U, output.size());
1595 EXPECT_EQ(SyncChange::ACTION_DELETE, output[0].change_type());
1596 const SyncData data(output[0].sync_data());
1597 EXPECT_EQ(tag1, syncer::SyncDataLocal(data).GetTag());
1598 for (int i = 1; i < 5; i++) {
1599 EXPECT_EQ(SyncChange::ACTION_DELETE, output[i].change_type());
1600 const SyncData data(output[i].sync_data());
1601 EXPECT_EQ(TabNodePool::TabIdToTag(tag1, i),
1602 syncer::SyncDataLocal(data).GetTag());
1605 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1606 ASSERT_EQ(1U, foreign_sessions.size());
1607 std::vector<std::vector<SessionID::id_type> > session_reference;
1608 session_reference.push_back(tab_list2);
1609 helper()->VerifySyncedSession(tag2, session_reference,
1610 *(foreign_sessions[0]));
1613 // Test that an update to a previously considered "stale" session,
1614 // prior to garbage collection, will save the session from deletion.
1615 TEST_F(SessionsSyncManagerTest, GarbageCollectionHonoursUpdate) {
1616 std::string tag1 = "tag1";
1617 SessionID::id_type n1[] = {5, 10, 13, 17};
1618 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
1619 std::vector<sync_pb::SessionSpecifics> tabs1;
1620 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1621 tag1, tab_list1, &tabs1));
1622 syncer::SyncDataList foreign_data;
1623 sync_pb::EntitySpecifics entity1;
1624 base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21);
1625 entity1.mutable_session()->CopyFrom(meta);
1626 foreign_data.push_back(SyncData::CreateRemoteData(
1630 syncer::AttachmentIdList(),
1631 syncer::AttachmentServiceProxyForTest::Create()));
1632 AddTabsToSyncDataList(tabs1, &foreign_data);
1633 syncer::SyncChangeList output;
1634 InitWithSyncDataTakeOutput(foreign_data, &output);
1635 ASSERT_EQ(2U, output.size());
1637 // Update to a non-stale time.
1638 sync_pb::EntitySpecifics update_entity;
1639 update_entity.mutable_session()->CopyFrom(tabs1[0]);
1640 syncer::SyncChangeList changes;
1642 syncer::SyncChange(FROM_HERE,
1643 SyncChange::ACTION_UPDATE,
1644 syncer::SyncData::CreateRemoteData(
1648 syncer::AttachmentIdList(),
1649 syncer::AttachmentServiceProxyForTest::Create())));
1650 manager()->ProcessSyncChanges(FROM_HERE, changes);
1652 // Check that the foreign session was associated and retrieve the data.
1653 std::vector<const SyncedSession*> foreign_sessions;
1654 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1655 ASSERT_EQ(1U, foreign_sessions.size());
1656 foreign_sessions.clear();
1658 // Verify the now non-stale session does not get deleted.
1659 manager()->DoGarbageCollection();
1660 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1661 ASSERT_EQ(1U, foreign_sessions.size());
1662 std::vector<std::vector<SessionID::id_type> > session_reference;
1663 session_reference.push_back(tab_list1);
1664 helper()->VerifySyncedSession(
1665 tag1, session_reference, *(foreign_sessions[0]));
1668 // Test that swapping WebContents for a tab is properly observed and handled
1669 // by the SessionsSyncManager.
1670 TEST_F(SessionsSyncManagerTest, CheckPrerenderedWebContentsSwap) {
1671 AddTab(browser(), GURL("http://foo1"));
1672 NavigateAndCommitActiveTab(GURL("http://foo2"));
1674 syncer::SyncChangeList out;
1675 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1676 ASSERT_EQ(4U, out.size()); // Header, tab ADD, tab UPDATE, header UPDATE.
1678 // To simulate WebContents swap during prerendering, create new WebContents
1679 // and swap with old WebContents.
1680 scoped_ptr<content::WebContents> old_web_contents;
1681 old_web_contents.reset(browser()->tab_strip_model()->GetActiveWebContents());
1683 // Create new WebContents, with the required tab helpers.
1684 WebContents* new_web_contents = WebContents::CreateWithSessionStorage(
1685 WebContents::CreateParams(profile()),
1686 old_web_contents->GetController().GetSessionStorageNamespaceMap());
1687 SessionTabHelper::CreateForWebContents(new_web_contents);
1688 TabContentsSyncedTabDelegate::CreateForWebContents(new_web_contents);
1689 new_web_contents->GetController()
1690 .CopyStateFrom(old_web_contents->GetController());
1692 // Swap the WebContents.
1693 int index = browser()->tab_strip_model()->GetIndexOfWebContents(
1694 old_web_contents.get());
1695 browser()->tab_strip_model()->ReplaceWebContentsAt(index, new_web_contents);
1697 ASSERT_EQ(9U, out.size());
1698 EXPECT_EQ(SyncChange::ACTION_ADD, out[4].change_type());
1699 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type());
1702 NavigateAndCommitActiveTab(GURL("http://bar2"));
1704 // Delete old WebContents. This should not crash.
1705 old_web_contents.reset();
1707 // Try more navigations and verify output size. This can also reveal
1708 // bugs (leaks) on memcheck bots if the SessionSyncManager
1709 // didn't properly clean up the tab pool or session tracker.
1710 NavigateAndCommitActiveTab(GURL("http://bar3"));
1712 AddTab(browser(), GURL("http://bar4"));
1713 NavigateAndCommitActiveTab(GURL("http://bar5"));
1714 ASSERT_EQ(19U, out.size());
1718 class SessionNotificationObserver : public content::NotificationObserver {
1720 SessionNotificationObserver() : notified_of_update_(false),
1721 notified_of_refresh_(false) {
1722 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
1723 content::NotificationService::AllSources());
1724 registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
1725 content::NotificationService::AllSources());
1727 virtual void Observe(int type,
1728 const content::NotificationSource& source,
1729 const content::NotificationDetails& details) OVERRIDE {
1731 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
1732 notified_of_update_ = true;
1734 case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL:
1735 notified_of_refresh_ = true;
1742 bool notified_of_update() const { return notified_of_update_; }
1743 bool notified_of_refresh() const { return notified_of_refresh_; }
1745 notified_of_update_ = false;
1746 notified_of_refresh_ = false;
1749 content::NotificationRegistrar registrar_;
1750 bool notified_of_update_;
1751 bool notified_of_refresh_;
1755 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when processing
1757 TEST_F(SessionsSyncManagerTest, NotifiedOfUpdates) {
1758 SessionNotificationObserver observer;
1759 ASSERT_FALSE(observer.notified_of_update());
1760 InitWithNoSyncData();
1762 SessionID::id_type n[] = {5};
1763 std::vector<sync_pb::SessionSpecifics> tabs1;
1764 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
1765 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1766 "tag1", tab_list, &tabs1));
1768 syncer::SyncChangeList changes;
1769 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1770 manager()->ProcessSyncChanges(FROM_HERE, changes);
1771 EXPECT_TRUE(observer.notified_of_update());
1775 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1776 manager()->ProcessSyncChanges(FROM_HERE, changes);
1777 EXPECT_TRUE(observer.notified_of_update());
1781 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE));
1782 manager()->ProcessSyncChanges(FROM_HERE, changes);
1783 EXPECT_TRUE(observer.notified_of_update());
1786 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when handling
1787 // local hide/removal of foreign session.
1788 TEST_F(SessionsSyncManagerTest, NotifiedOfLocalRemovalOfForeignSession) {
1789 InitWithNoSyncData();
1790 const std::string tag("tag1");
1791 SessionID::id_type n[] = {5};
1792 std::vector<sync_pb::SessionSpecifics> tabs1;
1793 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
1794 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1795 tag, tab_list, &tabs1));
1797 syncer::SyncChangeList changes;
1798 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1799 manager()->ProcessSyncChanges(FROM_HERE, changes);
1801 SessionNotificationObserver observer;
1802 ASSERT_FALSE(observer.notified_of_update());
1803 manager()->DeleteForeignSession(tag);
1804 ASSERT_TRUE(observer.notified_of_update());
1807 #if defined(OS_ANDROID) || defined(OS_IOS)
1808 // Tests that opening the other devices page triggers a session sync refresh.
1809 // This page only exists on mobile platforms today; desktop has a
1810 // search-enhanced NTP without other devices.
1811 TEST_F(SessionsSyncManagerTest, NotifiedOfRefresh) {
1812 SessionNotificationObserver observer;
1813 ASSERT_FALSE(observer.notified_of_refresh());
1814 InitWithNoSyncData();
1815 AddTab(browser(), GURL("http://foo1"));
1816 EXPECT_FALSE(observer.notified_of_refresh());
1817 NavigateAndCommitActiveTab(GURL("chrome://newtab/#open_tabs"));
1818 EXPECT_TRUE(observer.notified_of_refresh());
1820 #endif // defined(OS_ANDROID) || defined(OS_IOS)
1822 } // namespace browser_sync