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