1 // Copyright 2013 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/sessions2/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/sessions2/notification_service_sessions_router.h"
17 #include "chrome/browser/sync/sessions2/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 "content/public/browser/navigation_entry.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/web_contents.h"
27 #include "sync/api/sync_error_factory_mock.h"
28 #include "testing/gmock/include/gmock/gmock.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 using content::WebContents;
32 using sessions::SerializedNavigationEntry;
33 using sessions::SerializedNavigationEntryTestHelper;
34 using syncer::SyncChange;
35 using syncer::SyncData;
37 namespace browser_sync {
41 class SyncedWindowDelegateOverride : public SyncedWindowDelegate {
43 explicit SyncedWindowDelegateOverride(SyncedWindowDelegate* wrapped)
46 virtual ~SyncedWindowDelegateOverride() {}
48 virtual bool HasWindow() const OVERRIDE {
49 return wrapped_->HasWindow();
52 virtual SessionID::id_type GetSessionId() const OVERRIDE {
53 return wrapped_->GetSessionId();
56 virtual int GetTabCount() const OVERRIDE {
57 return wrapped_->GetTabCount();
60 virtual int GetActiveIndex() const OVERRIDE {
61 return wrapped_->GetActiveIndex();
64 virtual bool IsApp() const OVERRIDE {
65 return wrapped_->IsApp();
68 virtual bool IsTypeTabbed() const OVERRIDE {
69 return wrapped_->IsTypeTabbed();
72 virtual bool IsTypePopup() const OVERRIDE {
73 return wrapped_->IsTypePopup();
76 virtual bool IsTabPinned(const SyncedTabDelegate* tab) const OVERRIDE {
77 return wrapped_->IsTabPinned(tab);
80 virtual SyncedTabDelegate* GetTabAt(int index) const OVERRIDE {
81 if (tab_overrides_.find(index) != tab_overrides_.end())
82 return tab_overrides_.find(index)->second;
84 return wrapped_->GetTabAt(index);
87 void OverrideTabAt(int index,
88 SyncedTabDelegate* delegate,
89 SessionID::id_type tab_id) {
90 tab_overrides_[index] = delegate;
91 tab_id_overrides_[index] = tab_id;
94 virtual SessionID::id_type GetTabIdAt(int index) const OVERRIDE {
95 if (tab_id_overrides_.find(index) != tab_id_overrides_.end())
96 return tab_id_overrides_.find(index)->second;
97 return wrapped_->GetTabIdAt(index);
100 virtual bool IsSessionRestoreInProgress() const OVERRIDE {
101 return wrapped_->IsSessionRestoreInProgress();
105 std::map<int, SyncedTabDelegate*> tab_overrides_;
106 std::map<int, SessionID::id_type> tab_id_overrides_;
107 SyncedWindowDelegate* wrapped_;
110 class TestSyncedWindowDelegatesGetter : public SyncedWindowDelegatesGetter {
112 TestSyncedWindowDelegatesGetter(
113 const std::set<SyncedWindowDelegate*>& delegates)
114 : delegates_(delegates) {}
116 virtual const std::set<SyncedWindowDelegate*> GetSyncedWindowDelegates()
121 const std::set<SyncedWindowDelegate*> delegates_;
124 class TestSyncProcessorStub : public syncer::SyncChangeProcessor {
126 explicit TestSyncProcessorStub(syncer::SyncChangeList* output)
128 virtual syncer::SyncError ProcessSyncChanges(
129 const tracked_objects::Location& from_here,
130 const syncer::SyncChangeList& change_list) OVERRIDE {
131 if (error_.IsSet()) {
132 syncer::SyncError error = error_;
133 error_ = syncer::SyncError();
138 output_->insert(output_->end(), change_list.begin(), change_list.end());
140 return syncer::SyncError();
143 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type)
145 return sync_data_to_return_;
148 void FailProcessSyncChangesWith(const syncer::SyncError& error) {
152 void SetSyncDataToReturn(const syncer::SyncDataList& data) {
153 sync_data_to_return_ = data;
157 syncer::SyncError error_;
158 syncer::SyncChangeList* output_;
159 syncer::SyncDataList sync_data_to_return_;
162 syncer::SyncChange MakeRemoteChange(
164 const sync_pb::SessionSpecifics& specifics,
165 SyncChange::SyncChangeType type) {
166 sync_pb::EntitySpecifics entity;
167 entity.mutable_session()->CopyFrom(specifics);
168 return syncer::SyncChange(
170 syncer::SyncData::CreateRemoteData(id, entity, base::Time()));
173 void AddTabsToChangeList(
174 const std::vector<sync_pb::SessionSpecifics>& batch,
175 SyncChange::SyncChangeType type,
176 syncer::SyncChangeList* change_list) {
177 std::vector<sync_pb::SessionSpecifics>::const_iterator iter;
178 for (iter = batch.begin();
179 iter != batch.end(); ++iter) {
180 sync_pb::EntitySpecifics entity;
181 entity.mutable_session()->CopyFrom(*iter);
182 change_list->push_back(syncer::SyncChange(
184 syncer::SyncData::CreateRemoteData(iter->tab_node_id(),
185 entity, base::Time())));
189 void AddTabsToSyncDataList(const std::vector<sync_pb::SessionSpecifics> tabs,
190 syncer::SyncDataList* list) {
191 for (size_t i = 0; i < tabs.size(); i++) {
192 sync_pb::EntitySpecifics entity;
193 entity.mutable_session()->CopyFrom(tabs[i]);
194 list->push_back(SyncData::CreateRemoteData(
195 i + 2, entity, base::Time()));
199 class DummyRouter : public LocalSessionEventRouter {
201 virtual ~DummyRouter() {}
202 virtual void StartRoutingTo(LocalSessionEventHandler* handler) OVERRIDE {}
203 virtual void Stop() OVERRIDE {}
206 scoped_ptr<LocalSessionEventRouter> NewDummyRouter() {
207 return scoped_ptr<LocalSessionEventRouter>(new DummyRouter());
212 class SessionsSyncManagerTest
213 : public BrowserWithTestWindowTest,
214 public SessionsSyncManager::SyncInternalApiDelegate {
216 SessionsSyncManagerTest() : test_processor_(NULL) {}
218 virtual void SetUp() OVERRIDE {
219 BrowserWithTestWindowTest::SetUp();
220 browser_sync::NotificationServiceSessionsRouter* router(
221 new browser_sync::NotificationServiceSessionsRouter(
222 profile(), syncer::SyncableService::StartSyncFlare()));
223 manager_.reset(new SessionsSyncManager(profile(), this,
224 scoped_ptr<LocalSessionEventRouter>(router)));
227 virtual void TearDown() OVERRIDE {
228 test_processor_ = NULL;
231 BrowserWithTestWindowTest::TearDown();
234 virtual scoped_ptr<DeviceInfo> GetLocalDeviceInfo() const OVERRIDE {
235 return scoped_ptr<DeviceInfo>(
236 new DeviceInfo(GetLocalSyncCacheGUID(),
237 "Wayne Gretzky's Hacking Box",
240 sync_pb::SyncEnums_DeviceType_TYPE_LINUX));
243 virtual std::string GetLocalSyncCacheGUID() const OVERRIDE {
247 SessionsSyncManager* manager() { return manager_.get(); }
248 SessionSyncTestHelper* helper() { return &helper_; }
250 void InitWithSyncDataTakeOutput(const syncer::SyncDataList& initial_data,
251 syncer::SyncChangeList* output) {
252 test_processor_ = new TestSyncProcessorStub(output);
253 syncer::SyncMergeResult result = manager_->MergeDataAndStartSyncing(
254 syncer::SESSIONS, initial_data,
255 scoped_ptr<syncer::SyncChangeProcessor>(test_processor_),
256 scoped_ptr<syncer::SyncErrorFactory>(
257 new syncer::SyncErrorFactoryMock()));
258 EXPECT_FALSE(result.error().IsSet());
261 void InitWithNoSyncData() {
262 InitWithSyncDataTakeOutput(syncer::SyncDataList(), NULL);
265 void TriggerProcessSyncChangesError() {
266 test_processor_->FailProcessSyncChangesWith(syncer::SyncError(
267 FROM_HERE, syncer::SyncError::DATATYPE_ERROR, "Error",
271 void SetSyncData(const syncer::SyncDataList& data) {
272 test_processor_->SetSyncDataToReturn(data);
275 syncer::SyncChangeList* FilterOutLocalHeaderChanges(
276 syncer::SyncChangeList* list) {
277 syncer::SyncChangeList::iterator it = list->begin();
279 while (it != list->end()) {
280 if (it->sync_data().GetTag() == manager_->current_machine_tag()) {
281 EXPECT_TRUE(SyncChange::ACTION_ADD == it->change_type() ||
282 SyncChange::ACTION_UPDATE == it->change_type());
283 it = list->erase(it);
294 scoped_ptr<SessionsSyncManager> manager_;
295 SessionSyncTestHelper helper_;
296 TestSyncProcessorStub* test_processor_;
299 // Test that the SyncSessionManager can properly fill in a SessionHeader.
300 TEST_F(SessionsSyncManagerTest, PopulateSessionHeader) {
301 sync_pb::SessionHeader header_s;
302 header_s.set_client_name("Client 1");
303 header_s.set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_WIN);
305 SyncedSession session;
306 base::Time time = base::Time::Now();
307 SessionsSyncManager::PopulateSessionHeaderFromSpecifics(
308 header_s, time, &session);
309 ASSERT_EQ("Client 1", session.session_name);
310 ASSERT_EQ(SyncedSession::TYPE_WIN, session.device_type);
311 ASSERT_EQ(time, session.modified_time);
314 // Test translation between protobuf types and chrome session types.
315 TEST_F(SessionsSyncManagerTest, PopulateSessionWindow) {
316 sync_pb::SessionWindow window_s;
318 window_s.set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
319 window_s.set_selected_tab_index(1);
321 std::string tag = "tag";
322 SyncedSession* session = manager()->session_tracker_.GetSession(tag);
323 manager()->session_tracker_.PutWindowInSession(tag, 0);
324 manager()->BuildSyncedSessionFromSpecifics(
325 tag, window_s, base::Time(), session->windows[0]);
326 ASSERT_EQ(1U, session->windows[0]->tabs.size());
327 ASSERT_EQ(1, session->windows[0]->selected_tab_index);
328 ASSERT_EQ(1, session->windows[0]->type);
329 ASSERT_EQ(1U, manager()->session_tracker_.num_synced_sessions());
331 manager()->session_tracker_.num_synced_tabs(std::string("tag")));
336 class SyncedTabDelegateFake : public SyncedTabDelegate {
338 SyncedTabDelegateFake() : current_entry_index_(0),
339 pending_entry_index_(-1),
342 blocked_navigations_(NULL) {}
343 virtual ~SyncedTabDelegateFake() {}
345 virtual int GetCurrentEntryIndex() const OVERRIDE {
346 return current_entry_index_;
348 void set_current_entry_index(int i) {
349 current_entry_index_ = i;
352 virtual content::NavigationEntry* GetEntryAtIndex(int i) const OVERRIDE {
353 const int size = entries_.size();
354 return (size < i + 1) ? NULL : entries_[i];
357 void AppendEntry(content::NavigationEntry* entry) {
358 entries_.push_back(entry);
361 virtual int GetEntryCount() const OVERRIDE {
362 return entries_.size();
365 virtual int GetPendingEntryIndex() const OVERRIDE {
366 return pending_entry_index_;
368 void set_pending_entry_index(int i) {
369 pending_entry_index_ = i;
372 virtual SessionID::id_type GetWindowId() const OVERRIDE {
373 return SessionID::id_type();
376 virtual SessionID::id_type GetSessionId() const OVERRIDE {
377 return SessionID::id_type();
380 virtual bool IsBeingDestroyed() const OVERRIDE { return false; }
381 virtual Profile* profile() const OVERRIDE { return NULL; }
382 virtual std::string GetExtensionAppId() const OVERRIDE {
383 return std::string();
385 virtual content::NavigationEntry* GetPendingEntry() const OVERRIDE {
388 virtual content::NavigationEntry* GetActiveEntry() const OVERRIDE {
391 virtual bool ProfileIsManaged() const OVERRIDE {
394 void set_is_managed(bool is_managed) { is_managed_ = is_managed; }
395 virtual const std::vector<const content::NavigationEntry*>*
396 GetBlockedNavigations() const OVERRIDE {
397 return blocked_navigations_;
399 void set_blocked_navigations(
400 std::vector<const content::NavigationEntry*>* navs) {
401 blocked_navigations_ = navs;
403 virtual bool IsPinned() const OVERRIDE {
406 virtual bool HasWebContents() const OVERRIDE {
409 virtual content::WebContents* GetWebContents() const OVERRIDE {
413 // Session sync related methods.
414 virtual int GetSyncId() const OVERRIDE {
417 virtual void SetSyncId(int sync_id) OVERRIDE {
422 current_entry_index_ = 0;
423 pending_entry_index_ = -1;
429 int current_entry_index_;
430 int pending_entry_index_;
433 std::vector<const content::NavigationEntry*>* blocked_navigations_;
434 ScopedVector<content::NavigationEntry> entries_;
439 // Test that we exclude tabs with only chrome:// and file:// schemed navigations
440 // from ShouldSyncTab(..).
441 TEST_F(SessionsSyncManagerTest, ValidTabs) {
442 SyncedTabDelegateFake tab;
444 // A null entry shouldn't crash.
445 tab.AppendEntry(NULL);
446 EXPECT_FALSE(manager()->ShouldSyncTab(tab));
449 // A chrome:// entry isn't valid.
450 content::NavigationEntry* entry(content::NavigationEntry::Create());
451 entry->SetVirtualURL(GURL("chrome://preferences/"));
452 tab.AppendEntry(entry);
453 EXPECT_FALSE(manager()->ShouldSyncTab(tab));
456 // A file:// entry isn't valid, even in addition to another entry.
457 content::NavigationEntry* entry2(content::NavigationEntry::Create());
458 entry2->SetVirtualURL(GURL("file://bla"));
459 tab.AppendEntry(entry2);
460 EXPECT_FALSE(manager()->ShouldSyncTab(tab));
462 // Add a valid scheme entry to tab, making the tab valid.
463 content::NavigationEntry* entry3(content::NavigationEntry::Create());
464 entry3->SetVirtualURL(GURL("http://www.google.com"));
465 tab.AppendEntry(entry3);
466 EXPECT_FALSE(manager()->ShouldSyncTab(tab));
469 // Make sure GetCurrentVirtualURL() returns the virtual URL of the pending
470 // entry if the current entry is pending.
471 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLPending) {
472 SyncedTabDelegateFake tab;
473 content::NavigationEntry* entry(content::NavigationEntry::Create());
474 entry->SetVirtualURL(GURL("http://www.google.com"));
475 tab.AppendEntry(entry);
476 EXPECT_EQ(entry->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab));
479 // Make sure GetCurrentVirtualURL() returns the virtual URL of the current
480 // entry if the current entry is non-pending.
481 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLNonPending) {
482 SyncedTabDelegateFake tab;
483 content::NavigationEntry* entry(content::NavigationEntry::Create());
484 entry->SetVirtualURL(GURL("http://www.google.com"));
485 tab.AppendEntry(entry);
486 EXPECT_EQ(entry->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab));
489 static const base::Time kTime1 = base::Time::FromInternalValue(100);
490 static const base::Time kTime2 = base::Time::FromInternalValue(105);
491 static const base::Time kTime3 = base::Time::FromInternalValue(110);
492 static const base::Time kTime4 = base::Time::FromInternalValue(120);
493 static const base::Time kTime5 = base::Time::FromInternalValue(130);
495 // Populate the mock tab delegate with some data and navigation
496 // entries and make sure that setting a SessionTab from it preserves
497 // those entries (and clobbers any existing data).
498 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegate) {
499 // Create a tab with three valid entries.
500 SyncedTabDelegateFake tab;
501 content::NavigationEntry* entry1(content::NavigationEntry::Create());
502 entry1->SetVirtualURL(GURL("http://www.google.com"));
503 entry1->SetTimestamp(kTime1);
504 entry1->SetHttpStatusCode(200);
505 content::NavigationEntry* entry2(content::NavigationEntry::Create());
506 entry2->SetVirtualURL(GURL("http://www.noodle.com"));
507 entry2->SetTimestamp(kTime2);
508 entry2->SetHttpStatusCode(201);
509 content::NavigationEntry* entry3(content::NavigationEntry::Create());
510 entry3->SetVirtualURL(GURL("http://www.doodle.com"));
511 entry3->SetTimestamp(kTime3);
512 entry3->SetHttpStatusCode(202);
514 tab.AppendEntry(entry1);
515 tab.AppendEntry(entry2);
516 tab.AppendEntry(entry3);
517 tab.set_current_entry_index(2);
519 SessionTab session_tab;
520 session_tab.window_id.set_id(1);
521 session_tab.tab_id.set_id(1);
522 session_tab.tab_visual_index = 1;
523 session_tab.current_navigation_index = 1;
524 session_tab.pinned = true;
525 session_tab.extension_app_id = "app id";
526 session_tab.user_agent_override = "override";
527 session_tab.timestamp = kTime5;
528 session_tab.navigations.push_back(
529 SerializedNavigationEntryTestHelper::CreateNavigation(
530 "http://www.example.com", "Example"));
531 session_tab.session_storage_persistent_id = "persistent id";
532 manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab);
534 EXPECT_EQ(0, session_tab.window_id.id());
535 EXPECT_EQ(0, session_tab.tab_id.id());
536 EXPECT_EQ(0, session_tab.tab_visual_index);
537 EXPECT_EQ(2, session_tab.current_navigation_index);
538 EXPECT_FALSE(session_tab.pinned);
539 EXPECT_TRUE(session_tab.extension_app_id.empty());
540 EXPECT_TRUE(session_tab.user_agent_override.empty());
541 EXPECT_EQ(kTime4, session_tab.timestamp);
542 ASSERT_EQ(3u, session_tab.navigations.size());
543 EXPECT_EQ(entry1->GetVirtualURL(),
544 session_tab.navigations[0].virtual_url());
545 EXPECT_EQ(entry2->GetVirtualURL(),
546 session_tab.navigations[1].virtual_url());
547 EXPECT_EQ(entry3->GetVirtualURL(),
548 session_tab.navigations[2].virtual_url());
549 EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
550 EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
551 EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
552 EXPECT_EQ(200, session_tab.navigations[0].http_status_code());
553 EXPECT_EQ(201, session_tab.navigations[1].http_status_code());
554 EXPECT_EQ(202, session_tab.navigations[2].http_status_code());
555 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
556 session_tab.navigations[0].blocked_state());
557 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
558 session_tab.navigations[1].blocked_state());
559 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
560 session_tab.navigations[2].blocked_state());
561 EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
564 // Tests that for managed users blocked navigations are recorded and marked as
565 // such, while regular navigations are marked as allowed.
566 TEST_F(SessionsSyncManagerTest, BlockedNavigations) {
567 SyncedTabDelegateFake tab;
568 content::NavigationEntry* entry1(content::NavigationEntry::Create());
569 entry1->SetVirtualURL(GURL("http://www.google.com"));
570 entry1->SetTimestamp(kTime1);
571 tab.AppendEntry(entry1);
573 content::NavigationEntry* entry2 = content::NavigationEntry::Create();
574 entry2->SetVirtualURL(GURL("http://blocked.com/foo"));
575 entry2->SetTimestamp(kTime2);
576 content::NavigationEntry* entry3 = content::NavigationEntry::Create();
577 entry3->SetVirtualURL(GURL("http://evil.com"));
578 entry3->SetTimestamp(kTime3);
579 ScopedVector<const content::NavigationEntry> blocked_navigations;
580 blocked_navigations.push_back(entry2);
581 blocked_navigations.push_back(entry3);
583 tab.set_is_managed(true);
584 tab.set_blocked_navigations(&blocked_navigations.get());
586 SessionTab session_tab;
587 session_tab.window_id.set_id(1);
588 session_tab.tab_id.set_id(1);
589 session_tab.tab_visual_index = 1;
590 session_tab.current_navigation_index = 1;
591 session_tab.pinned = true;
592 session_tab.extension_app_id = "app id";
593 session_tab.user_agent_override = "override";
594 session_tab.timestamp = kTime5;
595 session_tab.navigations.push_back(
596 SerializedNavigationEntryTestHelper::CreateNavigation(
597 "http://www.example.com", "Example"));
598 session_tab.session_storage_persistent_id = "persistent id";
599 manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab);
601 EXPECT_EQ(0, session_tab.window_id.id());
602 EXPECT_EQ(0, session_tab.tab_id.id());
603 EXPECT_EQ(0, session_tab.tab_visual_index);
604 EXPECT_EQ(0, session_tab.current_navigation_index);
605 EXPECT_FALSE(session_tab.pinned);
606 EXPECT_TRUE(session_tab.extension_app_id.empty());
607 EXPECT_TRUE(session_tab.user_agent_override.empty());
608 EXPECT_EQ(kTime4, session_tab.timestamp);
609 ASSERT_EQ(3u, session_tab.navigations.size());
610 EXPECT_EQ(entry1->GetVirtualURL(),
611 session_tab.navigations[0].virtual_url());
612 EXPECT_EQ(entry2->GetVirtualURL(),
613 session_tab.navigations[1].virtual_url());
614 EXPECT_EQ(entry3->GetVirtualURL(),
615 session_tab.navigations[2].virtual_url());
616 EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
617 EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
618 EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
619 EXPECT_EQ(SerializedNavigationEntry::STATE_ALLOWED,
620 session_tab.navigations[0].blocked_state());
621 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
622 session_tab.navigations[1].blocked_state());
623 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
624 session_tab.navigations[2].blocked_state());
625 EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
628 // Tests that the local session header objects is created properly in
629 // presence of no other session activity, once and only once.
630 TEST_F(SessionsSyncManagerTest, MergeLocalSessionNoTabs) {
631 syncer::SyncChangeList out;
632 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
633 EXPECT_FALSE(manager()->current_machine_tag().empty());
635 EXPECT_EQ(2U, out.size());
636 EXPECT_TRUE(out[0].IsValid());
637 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
638 const SyncData data(out[0].sync_data());
639 EXPECT_EQ(manager()->current_machine_tag(), data.GetTag());
640 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
641 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
642 EXPECT_TRUE(specifics.has_header());
643 const sync_pb::SessionHeader& header_s = specifics.header();
644 EXPECT_TRUE(header_s.has_device_type());
645 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
646 EXPECT_EQ(0, header_s.window_size());
648 EXPECT_TRUE(out[1].IsValid());
649 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
650 const SyncData data_2(out[1].sync_data());
651 EXPECT_EQ(manager()->current_machine_tag(), data_2.GetTag());
652 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
653 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
654 EXPECT_TRUE(specifics2.has_header());
655 const sync_pb::SessionHeader& header_s2 = specifics2.header();
656 EXPECT_EQ(0, header_s2.window_size());
658 // Now take that header node and feed it in as input.
659 SyncData d(SyncData::CreateRemoteData(1, data.GetSpecifics(), base::Time()));
660 syncer::SyncDataList in(&d, &d + 1);
662 SessionsSyncManager manager2(profile(), this, NewDummyRouter());
663 syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
664 syncer::SESSIONS, in,
665 scoped_ptr<syncer::SyncChangeProcessor>(
666 new TestSyncProcessorStub(&out)),
667 scoped_ptr<syncer::SyncErrorFactory>(
668 new syncer::SyncErrorFactoryMock()));
669 ASSERT_FALSE(result.error().IsSet());
671 EXPECT_EQ(1U, out.size());
672 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
673 EXPECT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
676 // Ensure model association associates the pre-existing tabs.
677 TEST_F(SessionsSyncManagerTest, SwappedOutOnRestore) {
678 AddTab(browser(), GURL("http://foo1"));
679 NavigateAndCommitActiveTab(GURL("http://foo2"));
680 AddTab(browser(), GURL("http://bar1"));
681 NavigateAndCommitActiveTab(GURL("http://bar2"));
682 AddTab(browser(), GURL("http://baz1"));
683 NavigateAndCommitActiveTab(GURL("http://baz2"));
684 const int kRestoredTabId = 1337;
685 const int kNewTabId = 2468;
687 syncer::SyncDataList in;
688 syncer::SyncChangeList out;
689 InitWithSyncDataTakeOutput(in, &out);
691 // Should be one header add, 3 tab add/update pairs, one header update.
692 ASSERT_EQ(8U, out.size());
694 // For input, we set up:
695 // * one "normal" fully loaded tab
696 // * one "frozen" tab with no WebContents and a tab_id change
697 // * one "frozen" tab with no WebContents and no tab_id change
698 SyncData t0(SyncData::CreateRemoteData(1, out[2].sync_data().GetSpecifics(),
700 sync_pb::EntitySpecifics entity(out[4].sync_data().GetSpecifics());
701 entity.mutable_session()->mutable_tab()->set_tab_id(kRestoredTabId);
702 SyncData t1(SyncData::CreateRemoteData(2, entity, base::Time()));
703 SyncData t2(SyncData::CreateRemoteData(3, out[6].sync_data().GetSpecifics(),
709 manager()->StopSyncing(syncer::SESSIONS);
711 const std::set<SyncedWindowDelegate*> windows(
712 SyncedWindowDelegate::GetSyncedWindowDelegates());
713 ASSERT_EQ(1U, windows.size());
714 SyncedTabDelegateFake t1_override, t2_override;
715 t1_override.SetSyncId(1); // No WebContents by default.
716 t2_override.SetSyncId(2); // No WebContents by default.
717 SyncedWindowDelegateOverride window_override(*windows.begin());
718 window_override.OverrideTabAt(1, &t1_override, kNewTabId);
719 window_override.OverrideTabAt(2, &t2_override,
720 t2.GetSpecifics().session().tab().tab_id());
721 std::set<SyncedWindowDelegate*> delegates;
722 delegates.insert(&window_override);
723 scoped_ptr<TestSyncedWindowDelegatesGetter> getter(
724 new TestSyncedWindowDelegatesGetter(delegates));
725 manager()->synced_window_getter_.reset(getter.release());
727 syncer::SyncMergeResult result = manager()->MergeDataAndStartSyncing(
728 syncer::SESSIONS, in,
729 scoped_ptr<syncer::SyncChangeProcessor>(
730 new TestSyncProcessorStub(&out)),
731 scoped_ptr<syncer::SyncErrorFactory>(
732 new syncer::SyncErrorFactoryMock()));
734 // There should be two changes, one for the fully associated tab, and
735 // one for the tab_id update to t1. t2 shouldn't need to be updated.
736 ASSERT_EQ(2U, FilterOutLocalHeaderChanges(&out)->size());
737 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
738 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
740 out[1].sync_data().GetSpecifics().session().tab().tab_id());
743 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
744 ASSERT_EQ(3U, tab_map.size());
745 int t2_tab_id = t2.GetSpecifics().session().tab().tab_id();
746 EXPECT_EQ(2, tab_map.find(t2_tab_id)->second->tab_node_id());
747 EXPECT_EQ(1, tab_map.find(kNewTabId)->second->tab_node_id());
748 int t0_tab_id = out[0].sync_data().GetSpecifics().session().tab().tab_id();
749 EXPECT_EQ(0, tab_map.find(t0_tab_id)->second->tab_node_id());
750 // TODO(tim): Once bug 337057 is fixed, we can issue an OnLocalTabModified
751 // from here (using an override similar to above) to return a new tab id
752 // and verify that we don't see any node creations in the SyncChangeProcessor
753 // (similar to how SessionsSyncManagerTest.OnLocalTabModified works.)
756 // Tests MergeDataAndStartSyncing with sync data but no local data.
757 TEST_F(SessionsSyncManagerTest, MergeWithInitialForeignSession) {
758 std::string tag = "tag1";
760 SessionID::id_type n1[] = {5, 10, 13, 17};
761 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
762 std::vector<sync_pb::SessionSpecifics> tabs1;
763 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
764 tag, tab_list1, &tabs1));
765 // Add a second window.
766 SessionID::id_type n2[] = {7, 15, 18, 20};
767 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
768 helper()->AddWindowSpecifics(1, tab_list2, &meta);
770 // Set up initial data.
771 syncer::SyncDataList initial_data;
772 sync_pb::EntitySpecifics entity;
773 entity.mutable_session()->CopyFrom(meta);
774 initial_data.push_back(SyncData::CreateRemoteData(1, entity, base::Time()));
775 AddTabsToSyncDataList(tabs1, &initial_data);
777 for (size_t i = 0; i < tab_list2.size(); ++i) {
778 sync_pb::EntitySpecifics entity;
779 helper()->BuildTabSpecifics(tag, 0, tab_list2[i],
780 entity.mutable_session());
781 initial_data.push_back(
782 SyncData::CreateRemoteData(i + 10, entity, base::Time()));
785 syncer::SyncChangeList output;
786 InitWithSyncDataTakeOutput(initial_data, &output);
787 EXPECT_TRUE(FilterOutLocalHeaderChanges(&output)->empty());
789 std::vector<const SyncedSession*> foreign_sessions;
790 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
791 ASSERT_EQ(1U, foreign_sessions.size());
792 std::vector<std::vector<SessionID::id_type> > session_reference;
793 session_reference.push_back(tab_list1);
794 session_reference.push_back(tab_list2);
795 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
798 // This is a combination of MergeWithInitialForeignSession and
799 // MergeLocalSessionExistingTabs. We repeat some checks performed in each of
800 // those tests to ensure the common mixed scenario works.
801 TEST_F(SessionsSyncManagerTest, MergeWithLocalAndForeignTabs) {
803 AddTab(browser(), GURL("http://foo1"));
804 NavigateAndCommitActiveTab(GURL("http://foo2"));
807 std::string tag = "tag1";
808 SessionID::id_type n1[] = {5, 10, 13, 17};
809 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
810 std::vector<sync_pb::SessionSpecifics> tabs1;
811 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
812 tag, tab_list1, &tabs1));
813 syncer::SyncDataList foreign_data;
814 sync_pb::EntitySpecifics entity;
815 entity.mutable_session()->CopyFrom(meta);
816 foreign_data.push_back(SyncData::CreateRemoteData(1, entity, base::Time()));
817 AddTabsToSyncDataList(tabs1, &foreign_data);
819 syncer::SyncChangeList output;
820 InitWithSyncDataTakeOutput(foreign_data, &output);
821 ASSERT_EQ(4U, output.size());
823 // Verify the local header.
824 EXPECT_TRUE(output[0].IsValid());
825 EXPECT_EQ(SyncChange::ACTION_ADD, output[0].change_type());
826 const SyncData data(output[0].sync_data());
827 EXPECT_EQ(manager()->current_machine_tag(), data.GetTag());
828 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
829 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
830 EXPECT_TRUE(specifics.has_header());
831 const sync_pb::SessionHeader& header_s = specifics.header();
832 EXPECT_TRUE(header_s.has_device_type());
833 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
834 EXPECT_EQ(0, header_s.window_size());
836 // Verify the tab node creations and updates with content.
837 for (int i = 1; i < 3; i++) {
838 EXPECT_TRUE(output[i].IsValid());
839 const SyncData data(output[i].sync_data());
840 EXPECT_TRUE(StartsWithASCII(data.GetTag(),
841 manager()->current_machine_tag(), true));
842 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
843 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
845 EXPECT_EQ(SyncChange::ACTION_ADD, output[1].change_type());
846 EXPECT_EQ(SyncChange::ACTION_UPDATE, output[2].change_type());
847 EXPECT_TRUE(output[2].sync_data().GetSpecifics().session().has_tab());
849 // Verify the header was updated to reflect window state.
850 EXPECT_TRUE(output[3].IsValid());
851 EXPECT_EQ(SyncChange::ACTION_UPDATE, output[3].change_type());
852 const SyncData data_2(output[3].sync_data());
853 EXPECT_EQ(manager()->current_machine_tag(), data_2.GetTag());
854 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
855 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
856 EXPECT_TRUE(specifics2.has_header());
857 const sync_pb::SessionHeader& header_s2 = specifics2.header();
858 EXPECT_EQ(1, header_s2.window_size());
860 // Verify foreign data.
861 std::vector<const SyncedSession*> foreign_sessions;
862 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
863 std::vector<std::vector<SessionID::id_type> > session_reference;
864 session_reference.push_back(tab_list1);
865 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
866 // There should be one and only one foreign session. If VerifySyncedSession
867 // was successful above this EXPECT call ensures the local session didn't
868 // get mistakenly added to foreign tracking (Similar to ExistingTabs test).
869 EXPECT_EQ(1U, foreign_sessions.size());
872 // Tests the common scenario. Merge with both local and foreign session data
873 // followed by updates flowing from sync and local.
874 TEST_F(SessionsSyncManagerTest, UpdatesAfterMixedMerge) {
875 // Add local and foreign data.
876 AddTab(browser(), GURL("http://foo1"));
877 NavigateAndCommitActiveTab(GURL("http://foo2"));
879 std::string tag1 = "tag1";
880 syncer::SyncDataList foreign_data1;
881 std::vector<std::vector<SessionID::id_type> > meta1_reference;
882 sync_pb::SessionSpecifics meta1;
884 SessionID::id_type n1[] = {5, 10, 13, 17};
885 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
886 meta1_reference.push_back(tab_list1);
887 std::vector<sync_pb::SessionSpecifics> tabs1;
888 meta1 = helper()->BuildForeignSession(tag1, tab_list1, &tabs1);
889 sync_pb::EntitySpecifics entity;
890 entity.mutable_session()->CopyFrom(meta1);
891 foreign_data1.push_back(SyncData::CreateRemoteData(
892 1, entity, base::Time()));
893 AddTabsToSyncDataList(tabs1, &foreign_data1);
895 syncer::SyncChangeList output1;
896 InitWithSyncDataTakeOutput(foreign_data1, &output1);
897 ASSERT_EQ(4U, output1.size());
899 // Add a second window to the foreign session.
900 // TODO(tim): Bug 98892. Add local window too when observers are hooked up.
901 SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
902 std::vector<SessionID::id_type> tab_list2(
903 tab_nums2, tab_nums2 + arraysize(tab_nums2));
904 meta1_reference.push_back(tab_list2);
905 helper()->AddWindowSpecifics(1, tab_list2, &meta1);
906 std::vector<sync_pb::SessionSpecifics> tabs2;
907 tabs2.resize(tab_list2.size());
908 for (size_t i = 0; i < tab_list2.size(); ++i) {
909 helper()->BuildTabSpecifics(tag1, 0, tab_list2[i], &tabs2[i]);
912 syncer::SyncChangeList changes;
913 changes.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE));
914 AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes);
915 manager()->ProcessSyncChanges(FROM_HERE, changes);
918 // Check that the foreign session was associated and retrieve the data.
919 std::vector<const SyncedSession*> foreign_sessions;
920 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
921 ASSERT_EQ(1U, foreign_sessions.size());
922 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
923 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
924 helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0]));
926 // Add a new foreign session.
927 std::string tag2 = "tag2";
928 SessionID::id_type n2[] = {107, 115};
929 std::vector<SessionID::id_type> tag2_tab_list(n2, n2 + arraysize(n2));
930 std::vector<sync_pb::SessionSpecifics> tag2_tabs;
931 sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession(
932 tag2, tag2_tab_list, &tag2_tabs));
933 changes.push_back(MakeRemoteChange(100, meta2, SyncChange::ACTION_ADD));
934 AddTabsToChangeList(tag2_tabs, SyncChange::ACTION_ADD, &changes);
936 manager()->ProcessSyncChanges(FROM_HERE, changes);
939 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
940 std::vector<std::vector<SessionID::id_type> > meta2_reference;
941 meta2_reference.push_back(tag2_tab_list);
942 ASSERT_EQ(2U, foreign_sessions.size());
943 ASSERT_EQ(2U, foreign_sessions[1]->windows.find(0)->second->tabs.size());
944 helper()->VerifySyncedSession(tag2, meta2_reference, *(foreign_sessions[1]));
945 foreign_sessions.clear();
947 // Remove a tab from a window.
948 meta1_reference[0].pop_back();
949 tab_list1.pop_back();
950 sync_pb::SessionWindow* win = meta1.mutable_header()->mutable_window(0);
952 for (std::vector<int>::const_iterator iter = tab_list1.begin();
953 iter != tab_list1.end(); ++iter) {
956 syncer::SyncChangeList removal;
957 removal.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE));
958 AddTabsToChangeList(tabs1, SyncChange::ACTION_UPDATE, &removal);
959 manager()->ProcessSyncChanges(FROM_HERE, removal);
961 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
962 ASSERT_EQ(2U, foreign_sessions.size());
963 ASSERT_EQ(3U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
964 helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0]));
967 // Tests that this SyncSessionManager knows how to delete foreign sessions
969 TEST_F(SessionsSyncManagerTest, DeleteForeignSession) {
970 InitWithNoSyncData();
971 std::string tag = "tag1";
972 syncer::SyncChangeList changes;
974 std::vector<const SyncedSession*> foreign_sessions;
975 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
976 manager()->DeleteForeignSessionInternal(tag, &changes);
977 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
978 EXPECT_TRUE(changes.empty());
980 // Fill an instance of session specifics with a foreign session's data.
981 std::vector<sync_pb::SessionSpecifics> tabs;
982 SessionID::id_type n1[] = {5, 10, 13, 17};
983 std::vector<SessionID::id_type> tab_nums1(n1, n1 + arraysize(n1));
984 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
985 tag, tab_nums1, &tabs));
987 // Update associator with the session's meta node, window, and tabs.
988 manager()->UpdateTrackerWithForeignSession(meta, base::Time());
989 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs.begin();
990 iter != tabs.end(); ++iter) {
991 manager()->UpdateTrackerWithForeignSession(*iter, base::Time());
993 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
994 ASSERT_EQ(1U, foreign_sessions.size());
996 // Now delete the foreign session.
997 manager()->DeleteForeignSessionInternal(tag, &changes);
998 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1000 EXPECT_EQ(5U, changes.size());
1001 std::set<std::string> expected_tags(&tag, &tag + 1);
1002 for (int i = 0; i < 5; i++)
1003 expected_tags.insert(TabNodePool2::TabIdToTag(tag, i));
1005 for (int i = 0; i < 5; i++) {
1006 SCOPED_TRACE(changes[i].ToString());
1007 EXPECT_TRUE(changes[i].IsValid());
1008 EXPECT_EQ(SyncChange::ACTION_DELETE, changes[i].change_type());
1009 EXPECT_TRUE(changes[i].sync_data().IsValid());
1010 EXPECT_EQ(1U, expected_tags.erase(changes[i].sync_data().GetTag()));
1014 // Write a foreign session to a node, with the tabs arriving first, and then
1016 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeTabsFirst) {
1017 InitWithNoSyncData();
1019 // Fill an instance of session specifics with a foreign session's data.
1020 std::string tag = "tag1";
1021 SessionID::id_type nums1[] = {5, 10, 13, 17};
1022 std::vector<sync_pb::SessionSpecifics> tabs1;
1023 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
1024 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1025 tag, tab_list1, &tabs1));
1027 syncer::SyncChangeList adds;
1028 // Add tabs for first window, then the meta node.
1029 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &adds);
1030 adds.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1031 manager()->ProcessSyncChanges(FROM_HERE, adds);
1033 // Check that the foreign session was associated and retrieve the data.
1034 std::vector<const SyncedSession*> foreign_sessions;
1035 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1036 ASSERT_EQ(1U, foreign_sessions.size());
1037 std::vector<std::vector<SessionID::id_type> > session_reference;
1038 session_reference.push_back(tab_list1);
1039 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
1042 // Write a foreign session to a node with some tabs that never arrive.
1043 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeMissingTabs) {
1044 InitWithNoSyncData();
1046 // Fill an instance of session specifics with a foreign session's data.
1047 std::string tag = "tag1";
1048 SessionID::id_type nums1[] = {5, 10, 13, 17};
1049 std::vector<sync_pb::SessionSpecifics> tabs1;
1050 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
1051 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1052 tag, tab_list1, &tabs1));
1053 // Add a second window, but this time only create two tab nodes, despite the
1054 // window expecting four tabs.
1055 SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
1056 std::vector<SessionID::id_type> tab_list2(
1057 tab_nums2, tab_nums2 + arraysize(tab_nums2));
1058 helper()->AddWindowSpecifics(1, tab_list2, &meta);
1059 std::vector<sync_pb::SessionSpecifics> tabs2;
1061 for (size_t i = 0; i < 2; ++i) {
1062 helper()->BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
1065 syncer::SyncChangeList changes;
1066 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1067 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1068 AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes);
1069 manager()->ProcessSyncChanges(FROM_HERE, changes);
1072 // Check that the foreign session was associated and retrieve the data.
1073 std::vector<const SyncedSession*> foreign_sessions;
1074 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1075 ASSERT_EQ(1U, foreign_sessions.size());
1076 ASSERT_EQ(2U, foreign_sessions[0]->windows.size());
1077 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
1078 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
1080 // Close the second window.
1081 meta.mutable_header()->clear_window();
1082 helper()->AddWindowSpecifics(0, tab_list1, &meta);
1083 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_UPDATE));
1084 // Update associator with the session's meta node containing one window.
1085 manager()->ProcessSyncChanges(FROM_HERE, changes);
1087 // Check that the foreign session was associated and retrieve the data.
1088 foreign_sessions.clear();
1089 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1090 ASSERT_EQ(1U, foreign_sessions.size());
1091 ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
1092 std::vector<std::vector<SessionID::id_type> > session_reference;
1093 session_reference.push_back(tab_list1);
1094 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
1097 // Tests that the SessionsSyncManager can handle a remote client deleting
1098 // sync nodes that belong to this local session.
1099 TEST_F(SessionsSyncManagerTest, ProcessRemoteDeleteOfLocalSession) {
1100 syncer::SyncChangeList out;
1101 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1102 ASSERT_EQ(2U, out.size());
1103 sync_pb::EntitySpecifics entity(out[0].sync_data().GetSpecifics());
1104 SyncData d(SyncData::CreateRemoteData(1, entity, base::Time()));
1105 SetSyncData(syncer::SyncDataList(&d, &d + 1));
1108 syncer::SyncChangeList changes;
1110 MakeRemoteChange(1, entity.session(), SyncChange::ACTION_DELETE));
1111 manager()->ProcessSyncChanges(FROM_HERE, changes);
1112 EXPECT_TRUE(manager()->local_tab_pool_out_of_sync_);
1113 EXPECT_TRUE(out.empty()); // ChangeProcessor shouldn't see any activity.
1115 // This should trigger repair of the TabNodePool.
1116 const GURL foo1("http://foo/1");
1117 AddTab(browser(), foo1);
1118 EXPECT_FALSE(manager()->local_tab_pool_out_of_sync_);
1120 // AddTab triggers two notifications, one for the tab insertion and one for
1121 // committing the NavigationEntry. The first notification results in a tab
1122 // we don't associate although we do update the header node. The second
1123 // notification triggers association of the tab, and the subsequent window
1124 // update. So we should see 4 changes at the SyncChangeProcessor.
1125 ASSERT_EQ(4U, out.size());
1127 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
1128 ASSERT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
1129 EXPECT_EQ(SyncChange::ACTION_ADD, out[1].change_type());
1130 int tab_node_id = out[1].sync_data().GetSpecifics().session().tab_node_id();
1131 EXPECT_EQ(TabNodePool2::TabIdToTag(
1132 manager()->current_machine_tag(), tab_node_id),
1133 out[1].sync_data().GetTag());
1134 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
1135 ASSERT_TRUE(out[2].sync_data().GetSpecifics().session().has_tab());
1136 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[3].change_type());
1137 ASSERT_TRUE(out[3].sync_data().GetSpecifics().session().has_header());
1139 // Verify the actual content.
1140 const sync_pb::SessionHeader& session_header =
1141 out[3].sync_data().GetSpecifics().session().header();
1142 ASSERT_EQ(1, session_header.window_size());
1143 EXPECT_EQ(1, session_header.window(0).tab_size());
1144 const sync_pb::SessionTab& tab1 =
1145 out[2].sync_data().GetSpecifics().session().tab();
1146 ASSERT_EQ(1, tab1.navigation_size());
1147 EXPECT_EQ(foo1.spec(), tab1.navigation(0).virtual_url());
1149 // Verify TabNodePool integrity.
1150 EXPECT_EQ(1U, manager()->local_tab_pool_.Capacity());
1151 EXPECT_TRUE(manager()->local_tab_pool_.Empty());
1154 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
1155 ASSERT_EQ(1U, tab_map.size());
1156 int tab_id = out[2].sync_data().GetSpecifics().session().tab().tab_id();
1157 EXPECT_EQ(tab_node_id, tab_map.find(tab_id)->second->tab_node_id());
1160 // Test that receiving a session delete from sync removes the session
1162 TEST_F(SessionsSyncManagerTest, ProcessForeignDelete) {
1163 InitWithNoSyncData();
1164 SessionID::id_type n[] = {5};
1165 std::vector<sync_pb::SessionSpecifics> tabs1;
1166 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
1167 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1168 "tag1", tab_list, &tabs1));
1170 syncer::SyncChangeList changes;
1171 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1172 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1173 manager()->ProcessSyncChanges(FROM_HERE, changes);
1175 std::vector<const SyncedSession*> foreign_sessions;
1176 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1177 ASSERT_EQ(1U, foreign_sessions.size());
1180 foreign_sessions.clear();
1181 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE));
1182 manager()->ProcessSyncChanges(FROM_HERE, changes);
1184 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1187 // TODO(shashishekhar): "Move this to TabNodePool unittests."
1188 TEST_F(SessionsSyncManagerTest, SaveUnassociatedNodesForReassociation) {
1189 syncer::SyncChangeList changes;
1190 InitWithNoSyncData();
1192 std::string local_tag = manager()->current_machine_tag();
1193 // Create a free node and then dissassociate sessions so that it ends up
1195 manager()->local_tab_pool_.GetFreeTabNode(&changes);
1197 // Update the tab_id of the node, so that it is considered a valid
1198 // unassociated node otherwise it will be mistaken for a corrupted node and
1199 // will be deleted before being added to the tab node pool.
1200 sync_pb::EntitySpecifics entity(changes[0].sync_data().GetSpecifics());
1201 entity.mutable_session()->mutable_tab()->set_tab_id(1);
1202 SyncData d(SyncData::CreateRemoteData(1, entity, base::Time()));
1203 syncer::SyncDataList in(&d, &d + 1);
1205 SessionsSyncManager manager2(profile(), this, NewDummyRouter());
1206 syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
1207 syncer::SESSIONS, in,
1208 scoped_ptr<syncer::SyncChangeProcessor>(
1209 new TestSyncProcessorStub(&changes)),
1210 scoped_ptr<syncer::SyncErrorFactory>(
1211 new syncer::SyncErrorFactoryMock()));
1212 ASSERT_FALSE(result.error().IsSet());
1213 EXPECT_TRUE(FilterOutLocalHeaderChanges(&changes)->empty());
1216 TEST_F(SessionsSyncManagerTest, MergeDeletesCorruptNode) {
1217 syncer::SyncChangeList changes;
1218 InitWithNoSyncData();
1220 std::string local_tag = manager()->current_machine_tag();
1221 int tab_node_id = manager()->local_tab_pool_.GetFreeTabNode(&changes);
1222 SyncData d(SyncData::CreateRemoteData(
1223 1, changes[0].sync_data().GetSpecifics(), base::Time()));
1224 syncer::SyncDataList in(&d, &d + 1);
1228 InitWithSyncDataTakeOutput(in, &changes);
1229 EXPECT_EQ(1U, FilterOutLocalHeaderChanges(&changes)->size());
1230 EXPECT_EQ(SyncChange::ACTION_DELETE, changes[0].change_type());
1231 EXPECT_EQ(TabNodePool2::TabIdToTag(local_tag, tab_node_id),
1232 changes[0].sync_data().GetTag());
1235 // Test that things work if a tab is initially ignored.
1236 TEST_F(SessionsSyncManagerTest, AssociateWindowsDontReloadTabs) {
1237 syncer::SyncChangeList out;
1238 // Go to a URL that is ignored by session syncing.
1239 AddTab(browser(), GURL("chrome://preferences/"));
1240 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1241 ASSERT_EQ(2U, out.size()); // Header add and update.
1244 out[1].sync_data().GetSpecifics().session().header().window_size());
1247 // Go to a sync-interesting URL.
1248 NavigateAndCommitActiveTab(GURL("http://foo2"));
1250 EXPECT_EQ(3U, out.size()); // Tab add, update, and header update.
1252 EXPECT_TRUE(StartsWithASCII(out[0].sync_data().GetTag(),
1253 manager()->current_machine_tag(), true));
1254 EXPECT_EQ(manager()->current_machine_tag(),
1255 out[0].sync_data().GetSpecifics().session().session_tag());
1256 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
1258 EXPECT_TRUE(StartsWithASCII(out[1].sync_data().GetTag(),
1259 manager()->current_machine_tag(), true));
1260 EXPECT_EQ(manager()->current_machine_tag(),
1261 out[1].sync_data().GetSpecifics().session().session_tag());
1262 EXPECT_TRUE(out[1].sync_data().GetSpecifics().session().has_tab());
1263 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
1265 EXPECT_TRUE(out[2].IsValid());
1266 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
1267 const SyncData data(out[2].sync_data());
1268 EXPECT_EQ(manager()->current_machine_tag(), data.GetTag());
1269 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1270 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1271 EXPECT_TRUE(specifics.has_header());
1272 const sync_pb::SessionHeader& header_s = specifics.header();
1273 EXPECT_EQ(1, header_s.window_size());
1274 EXPECT_EQ(1, header_s.window(0).tab_size());
1277 // Tests that the SyncSessionManager responds to local tab events properly.
1278 TEST_F(SessionsSyncManagerTest, OnLocalTabModified) {
1279 syncer::SyncChangeList out;
1280 // Init with no local data, relies on MergeLocalSessionNoTabs.
1281 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1282 ASSERT_FALSE(manager()->current_machine_tag().empty());
1283 ASSERT_EQ(2U, out.size());
1285 // Copy the original header.
1286 sync_pb::EntitySpecifics header(out[0].sync_data().GetSpecifics());
1289 const GURL foo1("http://foo/1");
1290 const GURL foo2("http://foo/2");
1291 const GURL bar1("http://bar/1");
1292 const GURL bar2("http://bar/2");
1293 AddTab(browser(), foo1);
1294 NavigateAndCommitActiveTab(foo2);
1295 AddTab(browser(), bar1);
1296 NavigateAndCommitActiveTab(bar2);
1298 // One add, one update for each AddTab.
1299 // One update for each NavigateAndCommit.
1300 // = 6 total tab updates.
1301 // One header update corresponding to each of those.
1302 // = 6 total header updates.
1303 // 12 total updates.
1304 ASSERT_EQ(12U, out.size());
1306 // Verify the tab node creations and updates to ensure the SyncProcessor
1307 // sees the right operations.
1308 for (int i = 0; i < 12; i++) {
1310 EXPECT_TRUE(out[i].IsValid());
1311 const SyncData data(out[i].sync_data());
1312 EXPECT_TRUE(StartsWithASCII(data.GetTag(),
1313 manager()->current_machine_tag(), true));
1314 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1315 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1317 // First thing on an AddTab is a no-op header update for parented tab.
1318 EXPECT_EQ(header.SerializeAsString(),
1319 data.GetSpecifics().SerializeAsString());
1320 EXPECT_EQ(manager()->current_machine_tag(), data.GetTag());
1321 } else if (i % 6 == 1) {
1322 // Next, the TabNodePool should create the tab node.
1323 EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
1324 EXPECT_EQ(TabNodePool2::TabIdToTag(
1325 manager()->current_machine_tag(),
1326 data.GetSpecifics().session().tab_node_id()),
1328 } else if (i % 6 == 2) {
1329 // Then we see the tab update to the URL.
1330 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1331 EXPECT_EQ(TabNodePool2::TabIdToTag(
1332 manager()->current_machine_tag(),
1333 data.GetSpecifics().session().tab_node_id()),
1335 ASSERT_TRUE(specifics.has_tab());
1336 } else if (i % 6 == 3) {
1337 // The header needs to be updated to reflect the new window state.
1338 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1339 EXPECT_TRUE(specifics.has_header());
1340 } else if (i % 6 == 4) {
1341 // Now we move on to NavigateAndCommit. Update the tab.
1342 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1343 EXPECT_EQ(TabNodePool2::TabIdToTag(
1344 manager()->current_machine_tag(),
1345 data.GetSpecifics().session().tab_node_id()),
1347 ASSERT_TRUE(specifics.has_tab());
1348 } else if (i % 6 == 5) {
1349 // The header needs to be updated to reflect the new window state.
1350 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1351 ASSERT_TRUE(specifics.has_header());
1352 header = data.GetSpecifics();
1356 // Verify the actual content to ensure sync sees the right data.
1357 // When it's all said and done, the header should reflect two tabs.
1358 const sync_pb::SessionHeader& session_header = header.session().header();
1359 ASSERT_EQ(1, session_header.window_size());
1360 EXPECT_EQ(2, session_header.window(0).tab_size());
1362 // ASSERT_TRUEs above allow us to dive in freely here.
1363 // Verify first tab.
1364 const sync_pb::SessionTab& tab1_1 =
1365 out[2].sync_data().GetSpecifics().session().tab();
1366 ASSERT_EQ(1, tab1_1.navigation_size());
1367 EXPECT_EQ(foo1.spec(), tab1_1.navigation(0).virtual_url());
1368 const sync_pb::SessionTab& tab1_2 =
1369 out[4].sync_data().GetSpecifics().session().tab();
1370 ASSERT_EQ(2, tab1_2.navigation_size());
1371 EXPECT_EQ(foo1.spec(), tab1_2.navigation(0).virtual_url());
1372 EXPECT_EQ(foo2.spec(), tab1_2.navigation(1).virtual_url());
1374 // Verify second tab.
1375 const sync_pb::SessionTab& tab2_1 =
1376 out[8].sync_data().GetSpecifics().session().tab();
1377 ASSERT_EQ(1, tab2_1.navigation_size());
1378 EXPECT_EQ(bar1.spec(), tab2_1.navigation(0).virtual_url());
1379 const sync_pb::SessionTab& tab2_2 =
1380 out[10].sync_data().GetSpecifics().session().tab();
1381 ASSERT_EQ(2, tab2_2.navigation_size());
1382 EXPECT_EQ(bar1.spec(), tab2_2.navigation(0).virtual_url());
1383 EXPECT_EQ(bar2.spec(), tab2_2.navigation(1).virtual_url());
1386 // Ensure model association associates the pre-existing tabs.
1387 TEST_F(SessionsSyncManagerTest, MergeLocalSessionExistingTabs) {
1388 AddTab(browser(), GURL("http://foo1"));
1389 NavigateAndCommitActiveTab(GURL("http://foo2")); // Adds back entry.
1390 AddTab(browser(), GURL("http://bar1"));
1391 NavigateAndCommitActiveTab(GURL("http://bar2")); // Adds back entry.
1393 syncer::SyncChangeList out;
1394 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1395 ASSERT_EQ(6U, out.size());
1397 // Check that this machine's data is not included in the foreign windows.
1398 std::vector<const SyncedSession*> foreign_sessions;
1399 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1401 // Verify the header.
1402 EXPECT_TRUE(out[0].IsValid());
1403 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
1404 const SyncData data(out[0].sync_data());
1405 EXPECT_EQ(manager()->current_machine_tag(), data.GetTag());
1406 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1407 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1408 EXPECT_TRUE(specifics.has_header());
1409 const sync_pb::SessionHeader& header_s = specifics.header();
1410 EXPECT_TRUE(header_s.has_device_type());
1411 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
1412 EXPECT_EQ(0, header_s.window_size());
1414 // Verify the tab node creations and updates with content.
1415 for (int i = 1; i < 5; i++) {
1416 EXPECT_TRUE(out[i].IsValid());
1417 const SyncData data(out[i].sync_data());
1418 EXPECT_TRUE(StartsWithASCII(data.GetTag(),
1419 manager()->current_machine_tag(), true));
1420 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1421 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1423 EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
1425 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1426 EXPECT_TRUE(specifics.has_tab());
1430 // Verify the header was updated to reflect new window state.
1431 EXPECT_TRUE(out[5].IsValid());
1432 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type());
1433 const SyncData data_2(out[5].sync_data());
1434 EXPECT_EQ(manager()->current_machine_tag(), data_2.GetTag());
1435 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
1436 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
1437 EXPECT_TRUE(specifics2.has_header());
1438 const sync_pb::SessionHeader& header_s2 = specifics2.header();
1439 EXPECT_EQ(1, header_s2.window_size());
1442 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
1443 ASSERT_EQ(2U, tab_map.size());
1444 // Tabs are ordered by sessionid in tab_map, so should be able to traverse
1445 // the tree based on order of tabs created
1446 SessionsSyncManager::TabLinksMap::iterator iter = tab_map.begin();
1447 ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
1448 EXPECT_EQ(GURL("http://foo1"), iter->second->tab()->
1449 GetEntryAtIndex(0)->GetVirtualURL());
1450 EXPECT_EQ(GURL("http://foo2"), iter->second->tab()->
1451 GetEntryAtIndex(1)->GetVirtualURL());
1453 ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
1454 EXPECT_EQ(GURL("http://bar1"), iter->second->tab()->
1455 GetEntryAtIndex(0)->GetVirtualURL());
1456 EXPECT_EQ(GURL("http://bar2"), iter->second->tab()->
1457 GetEntryAtIndex(1)->GetVirtualURL());
1460 // Test garbage collection of stale foreign sessions.
1461 TEST_F(SessionsSyncManagerTest, DoGarbageCollection) {
1462 // Fill two instances of session specifics with a foreign session's data.
1463 std::string tag1 = "tag1";
1464 SessionID::id_type n1[] = {5, 10, 13, 17};
1465 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
1466 std::vector<sync_pb::SessionSpecifics> tabs1;
1467 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1468 tag1, tab_list1, &tabs1));
1469 std::string tag2 = "tag2";
1470 SessionID::id_type n2[] = {8, 15, 18, 20};
1471 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
1472 std::vector<sync_pb::SessionSpecifics> tabs2;
1473 sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession(
1474 tag2, tab_list2, &tabs2));
1475 // Set the modification time for tag1 to be 21 days ago, tag2 to 5 days ago.
1476 base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21);
1477 base::Time tag2_time = base::Time::Now() - base::TimeDelta::FromDays(5);
1479 syncer::SyncDataList foreign_data;
1480 sync_pb::EntitySpecifics entity1, entity2;
1481 entity1.mutable_session()->CopyFrom(meta);
1482 entity2.mutable_session()->CopyFrom(meta2);
1483 foreign_data.push_back(SyncData::CreateRemoteData(1, entity1, tag1_time));
1484 foreign_data.push_back(SyncData::CreateRemoteData(1, entity2, tag2_time));
1485 AddTabsToSyncDataList(tabs1, &foreign_data);
1486 AddTabsToSyncDataList(tabs2, &foreign_data);
1488 syncer::SyncChangeList output;
1489 InitWithSyncDataTakeOutput(foreign_data, &output);
1490 ASSERT_EQ(2U, output.size());
1493 // Check that the foreign session was associated and retrieve the data.
1494 std::vector<const SyncedSession*> foreign_sessions;
1495 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1496 ASSERT_EQ(2U, foreign_sessions.size());
1497 foreign_sessions.clear();
1499 // Now garbage collect and verify the non-stale session is still there.
1500 manager()->DoGarbageCollection();
1501 ASSERT_EQ(5U, output.size());
1502 EXPECT_EQ(SyncChange::ACTION_DELETE, output[0].change_type());
1503 const SyncData data(output[0].sync_data());
1504 EXPECT_EQ(tag1, data.GetTag());
1505 for (int i = 1; i < 5; i++) {
1506 EXPECT_EQ(SyncChange::ACTION_DELETE, output[i].change_type());
1507 const SyncData data(output[i].sync_data());
1508 EXPECT_EQ(TabNodePool2::TabIdToTag(tag1, i), data.GetTag());
1511 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1512 ASSERT_EQ(1U, foreign_sessions.size());
1513 std::vector<std::vector<SessionID::id_type> > session_reference;
1514 session_reference.push_back(tab_list2);
1515 helper()->VerifySyncedSession(tag2, session_reference,
1516 *(foreign_sessions[0]));
1519 // Test that an update to a previously considered "stale" session,
1520 // prior to garbage collection, will save the session from deletion.
1521 TEST_F(SessionsSyncManagerTest, GarbageCollectionHonoursUpdate) {
1522 std::string tag1 = "tag1";
1523 SessionID::id_type n1[] = {5, 10, 13, 17};
1524 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
1525 std::vector<sync_pb::SessionSpecifics> tabs1;
1526 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1527 tag1, tab_list1, &tabs1));
1528 syncer::SyncDataList foreign_data;
1529 sync_pb::EntitySpecifics entity1;
1530 base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21);
1531 entity1.mutable_session()->CopyFrom(meta);
1532 foreign_data.push_back(SyncData::CreateRemoteData(1, entity1, tag1_time));
1533 AddTabsToSyncDataList(tabs1, &foreign_data);
1534 syncer::SyncChangeList output;
1535 InitWithSyncDataTakeOutput(foreign_data, &output);
1536 ASSERT_EQ(2U, output.size());
1538 // Update to a non-stale time.
1539 sync_pb::EntitySpecifics update_entity;
1540 update_entity.mutable_session()->CopyFrom(tabs1[0]);
1541 syncer::SyncChangeList changes;
1542 changes.push_back(syncer::SyncChange(
1544 SyncChange::ACTION_UPDATE,
1545 syncer::SyncData::CreateRemoteData(1, update_entity,
1546 base::Time::Now())));
1547 manager()->ProcessSyncChanges(FROM_HERE, changes);
1549 // Check that the foreign session was associated and retrieve the data.
1550 std::vector<const SyncedSession*> foreign_sessions;
1551 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1552 ASSERT_EQ(1U, foreign_sessions.size());
1553 foreign_sessions.clear();
1555 // Verify the now non-stale session does not get deleted.
1556 manager()->DoGarbageCollection();
1557 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1558 ASSERT_EQ(1U, foreign_sessions.size());
1559 std::vector<std::vector<SessionID::id_type> > session_reference;
1560 session_reference.push_back(tab_list1);
1561 helper()->VerifySyncedSession(
1562 tag1, session_reference, *(foreign_sessions[0]));
1565 // Test that swapping WebContents for a tab is properly observed and handled
1566 // by the SessionsSyncManager.
1567 TEST_F(SessionsSyncManagerTest, CheckPrerenderedWebContentsSwap) {
1568 AddTab(browser(), GURL("http://foo1"));
1569 NavigateAndCommitActiveTab(GURL("http://foo2"));
1571 syncer::SyncChangeList out;
1572 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1573 ASSERT_EQ(4U, out.size()); // Header, tab ADD, tab UPDATE, header UPDATE.
1575 // To simulate WebContents swap during prerendering, create new WebContents
1576 // and swap with old WebContents.
1577 scoped_ptr<content::WebContents> old_web_contents;
1578 old_web_contents.reset(browser()->tab_strip_model()->GetActiveWebContents());
1580 // Create new WebContents, with the required tab helpers.
1581 WebContents* new_web_contents = WebContents::CreateWithSessionStorage(
1582 WebContents::CreateParams(profile()),
1583 old_web_contents->GetController().GetSessionStorageNamespaceMap());
1584 SessionTabHelper::CreateForWebContents(new_web_contents);
1585 TabContentsSyncedTabDelegate::CreateForWebContents(new_web_contents);
1586 new_web_contents->GetController()
1587 .CopyStateFrom(old_web_contents->GetController());
1589 // Swap the WebContents.
1590 int index = browser()->tab_strip_model()->GetIndexOfWebContents(
1591 old_web_contents.get());
1592 browser()->tab_strip_model()->ReplaceWebContentsAt(index, new_web_contents);
1594 ASSERT_EQ(9U, out.size());
1595 EXPECT_EQ(SyncChange::ACTION_ADD, out[4].change_type());
1596 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type());
1599 NavigateAndCommitActiveTab(GURL("http://bar2"));
1601 // Delete old WebContents. This should not crash.
1602 old_web_contents.reset();
1604 // Try more navigations and verify output size. This can also reveal
1605 // bugs (leaks) on memcheck bots if the SessionSyncManager
1606 // didn't properly clean up the tab pool or session tracker.
1607 NavigateAndCommitActiveTab(GURL("http://bar3"));
1609 AddTab(browser(), GURL("http://bar4"));
1610 NavigateAndCommitActiveTab(GURL("http://bar5"));
1611 ASSERT_EQ(19U, out.size());
1615 class SessionNotificationObserver : public content::NotificationObserver {
1617 SessionNotificationObserver() : notified_of_update_(false),
1618 notified_of_refresh_(false) {
1619 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
1620 content::NotificationService::AllSources());
1621 registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
1622 content::NotificationService::AllSources());
1624 virtual void Observe(int type,
1625 const content::NotificationSource& source,
1626 const content::NotificationDetails& details) OVERRIDE {
1628 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
1629 notified_of_update_ = true;
1631 case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL:
1632 notified_of_refresh_ = true;
1639 bool notified_of_update() const { return notified_of_update_; }
1640 bool notified_of_refresh() const { return notified_of_refresh_; }
1642 notified_of_update_ = false;
1643 notified_of_refresh_ = false;
1646 content::NotificationRegistrar registrar_;
1647 bool notified_of_update_;
1648 bool notified_of_refresh_;
1652 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent.
1653 TEST_F(SessionsSyncManagerTest, NotifiedOfUpdates) {
1654 SessionNotificationObserver observer;
1655 ASSERT_FALSE(observer.notified_of_update());
1656 InitWithNoSyncData();
1658 SessionID::id_type n[] = {5};
1659 std::vector<sync_pb::SessionSpecifics> tabs1;
1660 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
1661 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1662 "tag1", tab_list, &tabs1));
1664 syncer::SyncChangeList changes;
1665 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1666 manager()->ProcessSyncChanges(FROM_HERE, changes);
1667 EXPECT_TRUE(observer.notified_of_update());
1671 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1672 manager()->ProcessSyncChanges(FROM_HERE, changes);
1673 EXPECT_TRUE(observer.notified_of_update());
1677 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE));
1678 manager()->ProcessSyncChanges(FROM_HERE, changes);
1679 EXPECT_TRUE(observer.notified_of_update());
1682 #if defined(OS_ANDROID) || defined(OS_IOS)
1683 // Tests that opening the other devices page triggers a session sync refresh.
1684 // This page only exists on mobile platforms today; desktop has a
1685 // search-enhanced NTP without other devices.
1686 TEST_F(SessionsSyncManagerTest, NotifiedOfRefresh) {
1687 SessionNotificationObserver observer;
1688 ASSERT_FALSE(observer.notified_of_refresh());
1689 InitWithNoSyncData();
1690 AddTab(browser(), GURL("http://foo1"));
1691 EXPECT_FALSE(observer.notified_of_refresh());
1692 NavigateAndCommitActiveTab(GURL("chrome://newtab/#open_tabs"));
1693 EXPECT_TRUE(observer.notified_of_refresh());
1695 #endif // defined(OS_ANDROID) || defined(OS_IOS)
1697 } // namespace browser_sync