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