Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / sync / sessions / sessions_sync_manager_unittest.cc
blob70f8165e6403ebbe4366c539320801f861896fce
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/synced_window_delegates_getter.h"
15 #include "chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "chrome/test/base/browser_with_test_window_test.h"
18 #include "components/sessions/serialized_navigation_entry_test_helper.h"
19 #include "components/sessions/session_id.h"
20 #include "components/sessions/session_types.h"
21 #include "components/sync_driver/device_info.h"
22 #include "components/sync_driver/local_device_info_provider_mock.h"
23 #include "content/public/browser/navigation_entry.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_source.h"
27 #include "content/public/browser/web_contents.h"
28 #include "sync/api/attachments/attachment_id.h"
29 #include "sync/api/sync_error_factory_mock.h"
30 #include "sync/internal_api/public/attachments/attachment_service_proxy_for_test.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
34 using content::WebContents;
35 using sessions::SerializedNavigationEntry;
36 using sessions::SerializedNavigationEntryTestHelper;
37 using sync_driver::DeviceInfo;
38 using sync_driver::LocalDeviceInfoProvider;
39 using sync_driver::LocalDeviceInfoProviderMock;
40 using sync_driver::SyncedSession;
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::FromInternalValue(i + 1),
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(scoped_ptr<content::NavigationEntry> entry) {
381 entries_.push_back(entry.Pass());
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 void reset() {
423 current_entry_index_ = 0;
424 pending_entry_index_ = -1;
425 sync_id_ = -1;
426 entries_.clear();
429 private:
430 int current_entry_index_;
431 int pending_entry_index_;
432 bool is_supervised_;
433 int sync_id_;
434 std::vector<const content::NavigationEntry*>* blocked_navigations_;
435 ScopedVector<content::NavigationEntry> entries_;
438 } // namespace
440 // Make sure GetCurrentVirtualURL() returns the virtual URL of the pending
441 // entry if the current entry is pending.
442 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLPending) {
443 SyncedTabDelegateFake tab;
444 scoped_ptr<content::NavigationEntry> entry(
445 content::NavigationEntry::Create());
446 GURL url("http://www.google.com/");
447 entry->SetVirtualURL(url);
448 tab.AppendEntry(entry.Pass());
449 EXPECT_EQ(url, manager()->GetCurrentVirtualURL(tab));
452 // Make sure GetCurrentVirtualURL() returns the virtual URL of the current
453 // entry if the current entry is non-pending.
454 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLNonPending) {
455 SyncedTabDelegateFake tab;
456 scoped_ptr<content::NavigationEntry> entry(
457 content::NavigationEntry::Create());
458 GURL url("http://www.google.com/");
459 entry->SetVirtualURL(url);
460 tab.AppendEntry(entry.Pass());
461 EXPECT_EQ(url, manager()->GetCurrentVirtualURL(tab));
464 static const base::Time kTime0 = base::Time::FromInternalValue(100);
465 static const base::Time kTime1 = base::Time::FromInternalValue(110);
466 static const base::Time kTime2 = base::Time::FromInternalValue(120);
467 static const base::Time kTime3 = base::Time::FromInternalValue(130);
468 static const base::Time kTime4 = base::Time::FromInternalValue(140);
469 static const base::Time kTime5 = base::Time::FromInternalValue(150);
470 static const base::Time kTime6 = base::Time::FromInternalValue(160);
471 static const base::Time kTime7 = base::Time::FromInternalValue(170);
472 static const base::Time kTime8 = base::Time::FromInternalValue(180);
473 static const base::Time kTime9 = base::Time::FromInternalValue(190);
475 // Populate the mock tab delegate with some data and navigation
476 // entries and make sure that setting a SessionTab from it preserves
477 // those entries (and clobbers any existing data).
478 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegate) {
479 // Create a tab with three valid entries.
480 SyncedTabDelegateFake tab;
481 scoped_ptr<content::NavigationEntry> entry1(
482 content::NavigationEntry::Create());
483 GURL url1("http://www.google.com/");
484 entry1->SetVirtualURL(url1);
485 entry1->SetTimestamp(kTime1);
486 entry1->SetHttpStatusCode(200);
487 scoped_ptr<content::NavigationEntry> entry2(
488 content::NavigationEntry::Create());
489 GURL url2("http://www.noodle.com/");
490 entry2->SetVirtualURL(url2);
491 entry2->SetTimestamp(kTime2);
492 entry2->SetHttpStatusCode(201);
493 scoped_ptr<content::NavigationEntry> entry3(
494 content::NavigationEntry::Create());
495 GURL url3("http://www.doodle.com/");
496 entry3->SetVirtualURL(url3);
497 entry3->SetTimestamp(kTime3);
498 entry3->SetHttpStatusCode(202);
500 tab.AppendEntry(entry1.Pass());
501 tab.AppendEntry(entry2.Pass());
502 tab.AppendEntry(entry3.Pass());
503 tab.set_current_entry_index(2);
505 sessions::SessionTab session_tab;
506 session_tab.window_id.set_id(1);
507 session_tab.tab_id.set_id(1);
508 session_tab.tab_visual_index = 1;
509 session_tab.current_navigation_index = 1;
510 session_tab.pinned = true;
511 session_tab.extension_app_id = "app id";
512 session_tab.user_agent_override = "override";
513 session_tab.timestamp = kTime5;
514 session_tab.navigations.push_back(
515 SerializedNavigationEntryTestHelper::CreateNavigation(
516 "http://www.example.com", "Example"));
517 session_tab.session_storage_persistent_id = "persistent id";
518 manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab);
520 EXPECT_EQ(0, session_tab.window_id.id());
521 EXPECT_EQ(0, session_tab.tab_id.id());
522 EXPECT_EQ(0, session_tab.tab_visual_index);
523 EXPECT_EQ(2, session_tab.current_navigation_index);
524 EXPECT_FALSE(session_tab.pinned);
525 EXPECT_TRUE(session_tab.extension_app_id.empty());
526 EXPECT_TRUE(session_tab.user_agent_override.empty());
527 EXPECT_EQ(kTime4, session_tab.timestamp);
528 ASSERT_EQ(3u, session_tab.navigations.size());
529 EXPECT_EQ(url1, session_tab.navigations[0].virtual_url());
530 EXPECT_EQ(url2, session_tab.navigations[1].virtual_url());
531 EXPECT_EQ(url3, session_tab.navigations[2].virtual_url());
532 EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
533 EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
534 EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
535 EXPECT_EQ(200, session_tab.navigations[0].http_status_code());
536 EXPECT_EQ(201, session_tab.navigations[1].http_status_code());
537 EXPECT_EQ(202, session_tab.navigations[2].http_status_code());
538 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
539 session_tab.navigations[0].blocked_state());
540 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
541 session_tab.navigations[1].blocked_state());
542 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
543 session_tab.navigations[2].blocked_state());
544 EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
547 // Ensure the current_navigation_index gets set properly when the navigation
548 // stack gets trucated to +/- 6 entries.
549 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegateNavigationIndex) {
550 SyncedTabDelegateFake tab;
551 scoped_ptr<content::NavigationEntry> entry0(
552 content::NavigationEntry::Create());
553 GURL url0("http://www.google.com/");
554 entry0->SetVirtualURL(url0);
555 entry0->SetTimestamp(kTime0);
556 entry0->SetHttpStatusCode(200);
557 scoped_ptr<content::NavigationEntry> entry1(
558 content::NavigationEntry::Create());
559 GURL url1("http://www.zoogle.com/");
560 entry1->SetVirtualURL(url1);
561 entry1->SetTimestamp(kTime1);
562 entry1->SetHttpStatusCode(200);
563 scoped_ptr<content::NavigationEntry> entry2(
564 content::NavigationEntry::Create());
565 GURL url2("http://www.noogle.com/");
566 entry2->SetVirtualURL(url2);
567 entry2->SetTimestamp(kTime2);
568 entry2->SetHttpStatusCode(200);
569 scoped_ptr<content::NavigationEntry> entry3(
570 content::NavigationEntry::Create());
571 GURL url3("http://www.doogle.com/");
572 entry3->SetVirtualURL(url3);
573 entry3->SetTimestamp(kTime3);
574 entry3->SetHttpStatusCode(200);
575 scoped_ptr<content::NavigationEntry> entry4(
576 content::NavigationEntry::Create());
577 GURL url4("http://www.yoogle.com/");
578 entry4->SetVirtualURL(url4);
579 entry4->SetTimestamp(kTime4);
580 entry4->SetHttpStatusCode(200);
581 scoped_ptr<content::NavigationEntry> entry5(
582 content::NavigationEntry::Create());
583 GURL url5("http://www.foogle.com/");
584 entry5->SetVirtualURL(url5);
585 entry5->SetTimestamp(kTime5);
586 entry5->SetHttpStatusCode(200);
587 scoped_ptr<content::NavigationEntry> entry6(
588 content::NavigationEntry::Create());
589 GURL url6("http://www.boogle.com/");
590 entry6->SetVirtualURL(url6);
591 entry6->SetTimestamp(kTime6);
592 entry6->SetHttpStatusCode(200);
593 scoped_ptr<content::NavigationEntry> entry7(
594 content::NavigationEntry::Create());
595 GURL url7("http://www.moogle.com/");
596 entry7->SetVirtualURL(url7);
597 entry7->SetTimestamp(kTime7);
598 entry7->SetHttpStatusCode(200);
599 scoped_ptr<content::NavigationEntry> entry8(
600 content::NavigationEntry::Create());
601 GURL url8("http://www.poogle.com/");
602 entry8->SetVirtualURL(url8);
603 entry8->SetTimestamp(kTime8);
604 entry8->SetHttpStatusCode(200);
605 scoped_ptr<content::NavigationEntry> entry9(
606 content::NavigationEntry::Create());
607 GURL url9("http://www.roogle.com/");
608 entry9->SetVirtualURL(url9);
609 entry9->SetTimestamp(kTime9);
610 entry9->SetHttpStatusCode(200);
612 tab.AppendEntry(entry0.Pass());
613 tab.AppendEntry(entry1.Pass());
614 tab.AppendEntry(entry2.Pass());
615 tab.AppendEntry(entry3.Pass());
616 tab.AppendEntry(entry4.Pass());
617 tab.AppendEntry(entry5.Pass());
618 tab.AppendEntry(entry6.Pass());
619 tab.AppendEntry(entry7.Pass());
620 tab.AppendEntry(entry8.Pass());
621 tab.AppendEntry(entry9.Pass());
622 tab.set_current_entry_index(8);
624 sessions::SessionTab session_tab;
625 manager()->SetSessionTabFromDelegate(tab, kTime9, &session_tab);
627 EXPECT_EQ(6, session_tab.current_navigation_index);
628 ASSERT_EQ(8u, session_tab.navigations.size());
629 EXPECT_EQ(url2, session_tab.navigations[0].virtual_url());
630 EXPECT_EQ(url3, session_tab.navigations[1].virtual_url());
631 EXPECT_EQ(url4, session_tab.navigations[2].virtual_url());
634 // Ensure the current_navigation_index gets set to the end of the navigation
635 // stack if the current navigation is invalid.
636 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegateCurrentInvalid) {
637 SyncedTabDelegateFake tab;
638 scoped_ptr<content::NavigationEntry> entry0(
639 content::NavigationEntry::Create());
640 entry0->SetVirtualURL(GURL("http://www.google.com"));
641 entry0->SetTimestamp(kTime0);
642 entry0->SetHttpStatusCode(200);
643 scoped_ptr<content::NavigationEntry> entry1(
644 content::NavigationEntry::Create());
645 entry1->SetVirtualURL(GURL(""));
646 entry1->SetTimestamp(kTime1);
647 entry1->SetHttpStatusCode(200);
648 scoped_ptr<content::NavigationEntry> entry2(
649 content::NavigationEntry::Create());
650 entry2->SetVirtualURL(GURL("http://www.noogle.com"));
651 entry2->SetTimestamp(kTime2);
652 entry2->SetHttpStatusCode(200);
653 scoped_ptr<content::NavigationEntry> entry3(
654 content::NavigationEntry::Create());
655 entry3->SetVirtualURL(GURL("http://www.doogle.com"));
656 entry3->SetTimestamp(kTime3);
657 entry3->SetHttpStatusCode(200);
659 tab.AppendEntry(entry0.Pass());
660 tab.AppendEntry(entry1.Pass());
661 tab.AppendEntry(entry2.Pass());
662 tab.AppendEntry(entry3.Pass());
663 tab.set_current_entry_index(1);
665 sessions::SessionTab session_tab;
666 manager()->SetSessionTabFromDelegate(tab, kTime9, &session_tab);
668 EXPECT_EQ(2, session_tab.current_navigation_index);
669 ASSERT_EQ(3u, session_tab.navigations.size());
672 // Tests that variation ids are set correctly.
673 TEST_F(SessionsSyncManagerTest, SetVariationIds) {
674 // Create two trials with a group which has a variation id for Chrome Sync
675 // and one with a variation id for another service.
676 const variations::VariationID kVariationId1 = 3300200;
677 const variations::VariationID kVariationId2 = 3300300;
678 const variations::VariationID kVariationId3 = 3300400;
680 base::FieldTrialList field_trial_list(NULL);
681 CreateAndActivateFieldTrial("trial name 1", "group name", kVariationId1,
682 variations::CHROME_SYNC_SERVICE);
683 CreateAndActivateFieldTrial("trial name 2", "group name", kVariationId2,
684 variations::CHROME_SYNC_SERVICE);
685 CreateAndActivateFieldTrial("trial name 3", "group name", kVariationId3,
686 variations::GOOGLE_UPDATE_SERVICE);
688 sessions::SessionTab session_tab;
689 manager()->SetVariationIds(&session_tab);
691 ASSERT_EQ(2u, session_tab.variation_ids.size());
692 EXPECT_EQ(kVariationId1, session_tab.variation_ids[0]);
693 EXPECT_EQ(kVariationId2, session_tab.variation_ids[1]);
696 // Tests that for supervised users blocked navigations are recorded and marked
697 // as such, while regular navigations are marked as allowed.
698 TEST_F(SessionsSyncManagerTest, BlockedNavigations) {
699 SyncedTabDelegateFake tab;
700 scoped_ptr<content::NavigationEntry> entry1(
701 content::NavigationEntry::Create());
702 GURL url1("http://www.google.com/");
703 entry1->SetVirtualURL(url1);
704 entry1->SetTimestamp(kTime1);
705 tab.AppendEntry(entry1.Pass());
707 scoped_ptr<content::NavigationEntry> entry2(
708 content::NavigationEntry::Create());
709 GURL url2("http://blocked.com/foo");
710 entry2->SetVirtualURL(url2);
711 entry2->SetTimestamp(kTime2);
712 scoped_ptr<content::NavigationEntry> entry3(
713 content::NavigationEntry::Create());
714 GURL url3("http://evil.com/");
715 entry3->SetVirtualURL(url3);
716 entry3->SetTimestamp(kTime3);
717 ScopedVector<const content::NavigationEntry> blocked_navigations;
718 blocked_navigations.push_back(entry2.Pass());
719 blocked_navigations.push_back(entry3.Pass());
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(url1, session_tab.navigations[0].virtual_url());
749 EXPECT_EQ(url2, session_tab.navigations[1].virtual_url());
750 EXPECT_EQ(url3, session_tab.navigations[2].virtual_url());
751 EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
752 EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
753 EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
754 EXPECT_EQ(SerializedNavigationEntry::STATE_ALLOWED,
755 session_tab.navigations[0].blocked_state());
756 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
757 session_tab.navigations[1].blocked_state());
758 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
759 session_tab.navigations[2].blocked_state());
760 EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
763 // Tests that the local session header objects is created properly in
764 // presence of no other session activity, once and only once.
765 TEST_F(SessionsSyncManagerTest, MergeLocalSessionNoTabs) {
766 syncer::SyncChangeList out;
767 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
768 EXPECT_FALSE(manager()->current_machine_tag().empty());
770 EXPECT_EQ(2U, out.size());
771 EXPECT_TRUE(out[0].IsValid());
772 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
773 const SyncData data(out[0].sync_data());
774 EXPECT_EQ(manager()->current_machine_tag(),
775 syncer::SyncDataLocal(data).GetTag());
776 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
777 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
778 EXPECT_TRUE(specifics.has_header());
779 const sync_pb::SessionHeader& header_s = specifics.header();
780 EXPECT_TRUE(header_s.has_device_type());
781 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
782 EXPECT_EQ(0, header_s.window_size());
784 EXPECT_TRUE(out[1].IsValid());
785 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
786 const SyncData data_2(out[1].sync_data());
787 EXPECT_EQ(manager()->current_machine_tag(),
788 syncer::SyncDataLocal(data_2).GetTag());
789 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
790 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
791 EXPECT_TRUE(specifics2.has_header());
792 const sync_pb::SessionHeader& header_s2 = specifics2.header();
793 EXPECT_EQ(0, header_s2.window_size());
795 // Now take that header node and feed it in as input.
796 SyncData d(SyncData::CreateRemoteData(
798 data.GetSpecifics(),
799 base::Time(),
800 syncer::AttachmentIdList(),
801 syncer::AttachmentServiceProxyForTest::Create()));
802 syncer::SyncDataList in(&d, &d + 1);
803 out.clear();
804 SessionsSyncManager manager2(profile(), local_device(), NewDummyRouter());
805 syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
806 syncer::SESSIONS, in,
807 scoped_ptr<syncer::SyncChangeProcessor>(
808 new TestSyncProcessorStub(&out)),
809 scoped_ptr<syncer::SyncErrorFactory>(
810 new syncer::SyncErrorFactoryMock()));
811 ASSERT_FALSE(result.error().IsSet());
813 EXPECT_EQ(1U, out.size());
814 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
815 EXPECT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
818 // Ensure model association associates the pre-existing tabs.
819 TEST_F(SessionsSyncManagerTest, SwappedOutOnRestore) {
820 AddTab(browser(), GURL("http://foo1"));
821 NavigateAndCommitActiveTab(GURL("http://foo2"));
822 AddTab(browser(), GURL("http://bar1"));
823 NavigateAndCommitActiveTab(GURL("http://bar2"));
824 AddTab(browser(), GURL("http://baz1"));
825 NavigateAndCommitActiveTab(GURL("http://baz2"));
826 const int kRestoredTabId = 1337;
827 const int kNewTabId = 2468;
829 syncer::SyncDataList in;
830 syncer::SyncChangeList out;
831 InitWithSyncDataTakeOutput(in, &out);
833 // Should be one header add, 3 tab add/update pairs, one header update.
834 ASSERT_EQ(8U, out.size());
836 // For input, we set up:
837 // * one "normal" fully loaded tab
838 // * one "frozen" tab with no WebContents and a tab_id change
839 // * one "frozen" tab with no WebContents and no tab_id change
840 SyncData t0(SyncData::CreateRemoteData(
842 out[2].sync_data().GetSpecifics(),
843 base::Time(),
844 syncer::AttachmentIdList(),
845 syncer::AttachmentServiceProxyForTest::Create()));
846 sync_pb::EntitySpecifics entity(out[4].sync_data().GetSpecifics());
847 entity.mutable_session()->mutable_tab()->set_tab_id(kRestoredTabId);
848 SyncData t1(SyncData::CreateRemoteData(
850 entity,
851 base::Time(),
852 syncer::AttachmentIdList(),
853 syncer::AttachmentServiceProxyForTest::Create()));
854 SyncData t2(SyncData::CreateRemoteData(
856 out[6].sync_data().GetSpecifics(),
857 base::Time(),
858 syncer::AttachmentIdList(),
859 syncer::AttachmentServiceProxyForTest::Create()));
860 in.push_back(t0);
861 in.push_back(t1);
862 in.push_back(t2);
863 out.clear();
864 manager()->StopSyncing(syncer::SESSIONS);
866 const std::set<const SyncedWindowDelegate*>& windows =
867 SyncedWindowDelegate::GetAll();
868 ASSERT_EQ(1U, windows.size());
869 SyncedTabDelegateFake t1_override, t2_override;
870 t1_override.SetSyncId(1); // No WebContents by default.
871 t2_override.SetSyncId(2); // No WebContents by default.
872 SyncedWindowDelegateOverride window_override(*windows.begin());
873 window_override.OverrideTabAt(1, &t1_override, kNewTabId);
874 window_override.OverrideTabAt(2, &t2_override,
875 t2.GetSpecifics().session().tab().tab_id());
876 std::set<const SyncedWindowDelegate*> delegates;
877 delegates.insert(&window_override);
878 scoped_ptr<TestSyncedWindowDelegatesGetter> getter(
879 new TestSyncedWindowDelegatesGetter(delegates));
880 manager()->synced_window_getter_.reset(getter.release());
882 syncer::SyncMergeResult result = manager()->MergeDataAndStartSyncing(
883 syncer::SESSIONS, in,
884 scoped_ptr<syncer::SyncChangeProcessor>(
885 new TestSyncProcessorStub(&out)),
886 scoped_ptr<syncer::SyncErrorFactory>(
887 new syncer::SyncErrorFactoryMock()));
889 // There should be two changes, one for the fully associated tab, and
890 // one for the tab_id update to t1. t2 shouldn't need to be updated.
891 ASSERT_EQ(2U, FilterOutLocalHeaderChanges(&out)->size());
892 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
893 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
894 EXPECT_EQ(kNewTabId,
895 out[1].sync_data().GetSpecifics().session().tab().tab_id());
897 // Verify TabLinks.
898 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
899 ASSERT_EQ(3U, tab_map.size());
900 int t2_tab_id = t2.GetSpecifics().session().tab().tab_id();
901 EXPECT_EQ(2, tab_map.find(t2_tab_id)->second->tab_node_id());
902 EXPECT_EQ(1, tab_map.find(kNewTabId)->second->tab_node_id());
903 int t0_tab_id = out[0].sync_data().GetSpecifics().session().tab().tab_id();
904 EXPECT_EQ(0, tab_map.find(t0_tab_id)->second->tab_node_id());
905 // TODO(tim): Once bug 337057 is fixed, we can issue an OnLocalTabModified
906 // from here (using an override similar to above) to return a new tab id
907 // and verify that we don't see any node creations in the SyncChangeProcessor
908 // (similar to how SessionsSyncManagerTest.OnLocalTabModified works.)
911 // Tests MergeDataAndStartSyncing with sync data but no local data.
912 TEST_F(SessionsSyncManagerTest, MergeWithInitialForeignSession) {
913 std::string tag = "tag1";
915 SessionID::id_type n1[] = {5, 10, 13, 17};
916 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
917 std::vector<sync_pb::SessionSpecifics> tabs1;
918 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
919 tag, tab_list1, &tabs1));
920 // Add a second window.
921 SessionID::id_type n2[] = {7, 15, 18, 20};
922 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
923 helper()->AddWindowSpecifics(1, tab_list2, &meta);
925 // Set up initial data.
926 syncer::SyncDataList initial_data;
927 sync_pb::EntitySpecifics entity;
928 entity.mutable_session()->CopyFrom(meta);
929 initial_data.push_back(SyncData::CreateRemoteData(
931 entity,
932 base::Time(),
933 syncer::AttachmentIdList(),
934 syncer::AttachmentServiceProxyForTest::Create()));
935 AddTabsToSyncDataList(tabs1, &initial_data);
937 for (size_t i = 0; i < tab_list2.size(); ++i) {
938 sync_pb::EntitySpecifics entity;
939 helper()->BuildTabSpecifics(tag, 0, tab_list2[i],
940 entity.mutable_session());
941 initial_data.push_back(SyncData::CreateRemoteData(
942 i + 10,
943 entity,
944 base::Time(),
945 syncer::AttachmentIdList(),
946 syncer::AttachmentServiceProxyForTest::Create()));
949 syncer::SyncChangeList output;
950 InitWithSyncDataTakeOutput(initial_data, &output);
951 EXPECT_TRUE(FilterOutLocalHeaderChanges(&output)->empty());
953 std::vector<const SyncedSession*> foreign_sessions;
954 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
955 ASSERT_EQ(1U, foreign_sessions.size());
956 std::vector<std::vector<SessionID::id_type> > session_reference;
957 session_reference.push_back(tab_list1);
958 session_reference.push_back(tab_list2);
959 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
962 // This is a combination of MergeWithInitialForeignSession and
963 // MergeLocalSessionExistingTabs. We repeat some checks performed in each of
964 // those tests to ensure the common mixed scenario works.
965 TEST_F(SessionsSyncManagerTest, MergeWithLocalAndForeignTabs) {
966 // Local.
967 AddTab(browser(), GURL("http://foo1"));
968 NavigateAndCommitActiveTab(GURL("http://foo2"));
970 // Foreign.
971 std::string tag = "tag1";
972 SessionID::id_type n1[] = {5, 10, 13, 17};
973 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
974 std::vector<sync_pb::SessionSpecifics> tabs1;
975 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
976 tag, tab_list1, &tabs1));
977 syncer::SyncDataList foreign_data;
978 sync_pb::EntitySpecifics entity;
979 entity.mutable_session()->CopyFrom(meta);
980 foreign_data.push_back(SyncData::CreateRemoteData(
982 entity,
983 base::Time(),
984 syncer::AttachmentIdList(),
985 syncer::AttachmentServiceProxyForTest::Create()));
986 AddTabsToSyncDataList(tabs1, &foreign_data);
988 syncer::SyncChangeList output;
989 InitWithSyncDataTakeOutput(foreign_data, &output);
990 ASSERT_EQ(4U, output.size());
992 // Verify the local header.
993 EXPECT_TRUE(output[0].IsValid());
994 EXPECT_EQ(SyncChange::ACTION_ADD, output[0].change_type());
995 const SyncData data(output[0].sync_data());
996 EXPECT_EQ(manager()->current_machine_tag(),
997 syncer::SyncDataLocal(data).GetTag());
998 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
999 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1000 EXPECT_TRUE(specifics.has_header());
1001 const sync_pb::SessionHeader& header_s = specifics.header();
1002 EXPECT_TRUE(header_s.has_device_type());
1003 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
1004 EXPECT_EQ(0, header_s.window_size());
1006 // Verify the tab node creations and updates with content.
1007 for (int i = 1; i < 3; i++) {
1008 EXPECT_TRUE(output[i].IsValid());
1009 const SyncData data(output[i].sync_data());
1010 EXPECT_TRUE(base::StartsWith(syncer::SyncDataLocal(data).GetTag(),
1011 manager()->current_machine_tag(),
1012 base::CompareCase::SENSITIVE));
1013 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1014 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1016 EXPECT_EQ(SyncChange::ACTION_ADD, output[1].change_type());
1017 EXPECT_EQ(SyncChange::ACTION_UPDATE, output[2].change_type());
1018 EXPECT_TRUE(output[2].sync_data().GetSpecifics().session().has_tab());
1020 // Verify the header was updated to reflect window state.
1021 EXPECT_TRUE(output[3].IsValid());
1022 EXPECT_EQ(SyncChange::ACTION_UPDATE, output[3].change_type());
1023 const SyncData data_2(output[3].sync_data());
1024 EXPECT_EQ(manager()->current_machine_tag(),
1025 syncer::SyncDataLocal(data_2).GetTag());
1026 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
1027 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
1028 EXPECT_TRUE(specifics2.has_header());
1029 const sync_pb::SessionHeader& header_s2 = specifics2.header();
1030 EXPECT_EQ(1, header_s2.window_size());
1032 // Verify foreign data.
1033 std::vector<const SyncedSession*> foreign_sessions;
1034 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1035 std::vector<std::vector<SessionID::id_type> > session_reference;
1036 session_reference.push_back(tab_list1);
1037 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
1038 // There should be one and only one foreign session. If VerifySyncedSession
1039 // was successful above this EXPECT call ensures the local session didn't
1040 // get mistakenly added to foreign tracking (Similar to ExistingTabs test).
1041 EXPECT_EQ(1U, foreign_sessions.size());
1044 // Tests the common scenario. Merge with both local and foreign session data
1045 // followed by updates flowing from sync and local.
1046 TEST_F(SessionsSyncManagerTest, UpdatesAfterMixedMerge) {
1047 // Add local and foreign data.
1048 AddTab(browser(), GURL("http://foo1"));
1049 NavigateAndCommitActiveTab(GURL("http://foo2"));
1051 std::string tag1 = "tag1";
1052 syncer::SyncDataList foreign_data1;
1053 std::vector<std::vector<SessionID::id_type> > meta1_reference;
1054 sync_pb::SessionSpecifics meta1;
1056 SessionID::id_type n1[] = {5, 10, 13, 17};
1057 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
1058 meta1_reference.push_back(tab_list1);
1059 std::vector<sync_pb::SessionSpecifics> tabs1;
1060 meta1 = helper()->BuildForeignSession(tag1, tab_list1, &tabs1);
1061 sync_pb::EntitySpecifics entity;
1062 entity.mutable_session()->CopyFrom(meta1);
1063 foreign_data1.push_back(SyncData::CreateRemoteData(
1065 entity,
1066 base::Time(),
1067 syncer::AttachmentIdList(),
1068 syncer::AttachmentServiceProxyForTest::Create()));
1069 AddTabsToSyncDataList(tabs1, &foreign_data1);
1071 syncer::SyncChangeList output1;
1072 InitWithSyncDataTakeOutput(foreign_data1, &output1);
1073 ASSERT_EQ(4U, output1.size());
1075 // Add a second window to the foreign session.
1076 // TODO(tim): Bug 98892. Add local window too when observers are hooked up.
1077 SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
1078 std::vector<SessionID::id_type> tab_list2(
1079 tab_nums2, tab_nums2 + arraysize(tab_nums2));
1080 meta1_reference.push_back(tab_list2);
1081 helper()->AddWindowSpecifics(1, tab_list2, &meta1);
1082 std::vector<sync_pb::SessionSpecifics> tabs2;
1083 tabs2.resize(tab_list2.size());
1084 for (size_t i = 0; i < tab_list2.size(); ++i) {
1085 helper()->BuildTabSpecifics(tag1, 0, tab_list2[i], &tabs2[i]);
1088 syncer::SyncChangeList changes;
1089 changes.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE));
1090 AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes);
1091 manager()->ProcessSyncChanges(FROM_HERE, changes);
1092 changes.clear();
1094 // Check that the foreign session was associated and retrieve the data.
1095 std::vector<const SyncedSession*> foreign_sessions;
1096 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1097 ASSERT_EQ(1U, foreign_sessions.size());
1098 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
1099 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
1100 helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0]));
1102 // Add a new foreign session.
1103 std::string tag2 = "tag2";
1104 SessionID::id_type n2[] = {107, 115};
1105 std::vector<SessionID::id_type> tag2_tab_list(n2, n2 + arraysize(n2));
1106 std::vector<sync_pb::SessionSpecifics> tag2_tabs;
1107 sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession(
1108 tag2, tag2_tab_list, &tag2_tabs));
1109 changes.push_back(MakeRemoteChange(100, meta2, SyncChange::ACTION_ADD));
1110 AddTabsToChangeList(tag2_tabs, SyncChange::ACTION_ADD, &changes);
1112 manager()->ProcessSyncChanges(FROM_HERE, changes);
1113 changes.clear();
1115 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1116 std::vector<std::vector<SessionID::id_type> > meta2_reference;
1117 meta2_reference.push_back(tag2_tab_list);
1118 ASSERT_EQ(2U, foreign_sessions.size());
1119 ASSERT_EQ(2U, foreign_sessions[1]->windows.find(0)->second->tabs.size());
1120 helper()->VerifySyncedSession(tag2, meta2_reference, *(foreign_sessions[1]));
1121 foreign_sessions.clear();
1123 // Remove a tab from a window.
1124 meta1_reference[0].pop_back();
1125 tab_list1.pop_back();
1126 sync_pb::SessionWindow* win = meta1.mutable_header()->mutable_window(0);
1127 win->clear_tab();
1128 for (std::vector<int>::const_iterator iter = tab_list1.begin();
1129 iter != tab_list1.end(); ++iter) {
1130 win->add_tab(*iter);
1132 syncer::SyncChangeList removal;
1133 removal.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE));
1134 AddTabsToChangeList(tabs1, SyncChange::ACTION_UPDATE, &removal);
1135 manager()->ProcessSyncChanges(FROM_HERE, removal);
1137 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1138 ASSERT_EQ(2U, foreign_sessions.size());
1139 ASSERT_EQ(3U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
1140 helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0]));
1143 // Tests that this SyncSessionManager knows how to delete foreign sessions
1144 // if it wants to.
1145 TEST_F(SessionsSyncManagerTest, DeleteForeignSession) {
1146 InitWithNoSyncData();
1147 std::string tag = "tag1";
1148 syncer::SyncChangeList changes;
1150 std::vector<const SyncedSession*> foreign_sessions;
1151 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1152 manager()->DeleteForeignSessionInternal(tag, &changes);
1153 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1154 EXPECT_TRUE(changes.empty());
1156 // Fill an instance of session specifics with a foreign session's data.
1157 std::vector<sync_pb::SessionSpecifics> tabs;
1158 SessionID::id_type n1[] = {5, 10, 13, 17};
1159 std::vector<SessionID::id_type> tab_nums1(n1, n1 + arraysize(n1));
1160 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1161 tag, tab_nums1, &tabs));
1163 // Update associator with the session's meta node, window, and tabs.
1164 manager()->UpdateTrackerWithForeignSession(meta, base::Time());
1165 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs.begin();
1166 iter != tabs.end(); ++iter) {
1167 manager()->UpdateTrackerWithForeignSession(*iter, base::Time());
1169 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1170 ASSERT_EQ(1U, foreign_sessions.size());
1172 // Now delete the foreign session.
1173 manager()->DeleteForeignSessionInternal(tag, &changes);
1174 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1176 EXPECT_EQ(5U, changes.size());
1177 std::set<std::string> expected_tags(&tag, &tag + 1);
1178 for (int i = 0; i < 5; i++)
1179 expected_tags.insert(TabNodePool::TabIdToTag(tag, i));
1181 for (int i = 0; i < 5; i++) {
1182 SCOPED_TRACE(changes[i].ToString());
1183 EXPECT_TRUE(changes[i].IsValid());
1184 EXPECT_EQ(SyncChange::ACTION_DELETE, changes[i].change_type());
1185 EXPECT_TRUE(changes[i].sync_data().IsValid());
1186 EXPECT_EQ(1U,
1187 expected_tags.erase(
1188 syncer::SyncDataLocal(changes[i].sync_data()).GetTag()));
1192 // Write a foreign session to a node, with the tabs arriving first, and then
1193 // retrieve it.
1194 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeTabsFirst) {
1195 InitWithNoSyncData();
1197 // Fill an instance of session specifics with a foreign session's data.
1198 std::string tag = "tag1";
1199 SessionID::id_type nums1[] = {5, 10, 13, 17};
1200 std::vector<sync_pb::SessionSpecifics> tabs1;
1201 std::vector<SessionID::id_type> tab_list1(nums1, nums1 + arraysize(nums1));
1202 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1203 tag, tab_list1, &tabs1));
1205 syncer::SyncChangeList adds;
1206 // Add tabs for first window, then the meta node.
1207 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &adds);
1208 adds.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1209 manager()->ProcessSyncChanges(FROM_HERE, adds);
1211 // Check that the foreign session was associated and retrieve the data.
1212 std::vector<const SyncedSession*> foreign_sessions;
1213 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1214 ASSERT_EQ(1U, foreign_sessions.size());
1215 std::vector<std::vector<SessionID::id_type> > session_reference;
1216 session_reference.push_back(tab_list1);
1217 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
1220 // Write a foreign session to a node with some tabs that never arrive.
1221 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeMissingTabs) {
1222 InitWithNoSyncData();
1224 // Fill an instance of session specifics with a foreign session's data.
1225 std::string tag = "tag1";
1226 SessionID::id_type nums1[] = {5, 10, 13, 17};
1227 std::vector<sync_pb::SessionSpecifics> tabs1;
1228 std::vector<SessionID::id_type> tab_list1(nums1, nums1 + arraysize(nums1));
1229 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1230 tag, tab_list1, &tabs1));
1231 // Add a second window, but this time only create two tab nodes, despite the
1232 // window expecting four tabs.
1233 SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
1234 std::vector<SessionID::id_type> tab_list2(
1235 tab_nums2, tab_nums2 + arraysize(tab_nums2));
1236 helper()->AddWindowSpecifics(1, tab_list2, &meta);
1237 std::vector<sync_pb::SessionSpecifics> tabs2;
1238 tabs2.resize(2);
1239 for (size_t i = 0; i < 2; ++i) {
1240 helper()->BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
1243 syncer::SyncChangeList changes;
1244 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1245 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1246 AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes);
1247 manager()->ProcessSyncChanges(FROM_HERE, changes);
1248 changes.clear();
1250 // Check that the foreign session was associated and retrieve the data.
1251 std::vector<const SyncedSession*> foreign_sessions;
1252 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1253 ASSERT_EQ(1U, foreign_sessions.size());
1254 ASSERT_EQ(2U, foreign_sessions[0]->windows.size());
1255 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
1256 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
1258 // Close the second window.
1259 meta.mutable_header()->clear_window();
1260 helper()->AddWindowSpecifics(0, tab_list1, &meta);
1261 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_UPDATE));
1262 // Update associator with the session's meta node containing one window.
1263 manager()->ProcessSyncChanges(FROM_HERE, changes);
1265 // Check that the foreign session was associated and retrieve the data.
1266 foreign_sessions.clear();
1267 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1268 ASSERT_EQ(1U, foreign_sessions.size());
1269 ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
1270 std::vector<std::vector<SessionID::id_type> > session_reference;
1271 session_reference.push_back(tab_list1);
1272 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
1275 // Tests that the SessionsSyncManager can handle a remote client deleting
1276 // sync nodes that belong to this local session.
1277 TEST_F(SessionsSyncManagerTest, ProcessRemoteDeleteOfLocalSession) {
1278 syncer::SyncChangeList out;
1279 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1280 ASSERT_EQ(2U, out.size());
1281 sync_pb::EntitySpecifics entity(out[0].sync_data().GetSpecifics());
1282 SyncData d(SyncData::CreateRemoteData(
1284 entity,
1285 base::Time(),
1286 syncer::AttachmentIdList(),
1287 syncer::AttachmentServiceProxyForTest::Create()));
1288 SetSyncData(syncer::SyncDataList(&d, &d + 1));
1289 out.clear();
1291 syncer::SyncChangeList changes;
1292 changes.push_back(
1293 MakeRemoteChange(1, entity.session(), SyncChange::ACTION_DELETE));
1294 manager()->ProcessSyncChanges(FROM_HERE, changes);
1295 EXPECT_TRUE(manager()->local_tab_pool_out_of_sync_);
1296 EXPECT_TRUE(out.empty()); // ChangeProcessor shouldn't see any activity.
1298 // This should trigger repair of the TabNodePool.
1299 const GURL foo1("http://foo/1");
1300 AddTab(browser(), foo1);
1301 EXPECT_FALSE(manager()->local_tab_pool_out_of_sync_);
1303 // AddTab triggers two notifications, one for the tab insertion and one for
1304 // committing the NavigationEntry. The first notification results in a tab
1305 // we don't associate although we do update the header node. The second
1306 // notification triggers association of the tab, and the subsequent window
1307 // update. So we should see 4 changes at the SyncChangeProcessor.
1308 ASSERT_EQ(4U, out.size());
1310 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
1311 ASSERT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
1312 EXPECT_EQ(SyncChange::ACTION_ADD, out[1].change_type());
1313 int tab_node_id = out[1].sync_data().GetSpecifics().session().tab_node_id();
1314 EXPECT_EQ(TabNodePool::TabIdToTag(
1315 manager()->current_machine_tag(), tab_node_id),
1316 syncer::SyncDataLocal(out[1].sync_data()).GetTag());
1317 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
1318 ASSERT_TRUE(out[2].sync_data().GetSpecifics().session().has_tab());
1319 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[3].change_type());
1320 ASSERT_TRUE(out[3].sync_data().GetSpecifics().session().has_header());
1322 // Verify the actual content.
1323 const sync_pb::SessionHeader& session_header =
1324 out[3].sync_data().GetSpecifics().session().header();
1325 ASSERT_EQ(1, session_header.window_size());
1326 EXPECT_EQ(1, session_header.window(0).tab_size());
1327 const sync_pb::SessionTab& tab1 =
1328 out[2].sync_data().GetSpecifics().session().tab();
1329 ASSERT_EQ(1, tab1.navigation_size());
1330 EXPECT_EQ(foo1.spec(), tab1.navigation(0).virtual_url());
1332 // Verify TabNodePool integrity.
1333 EXPECT_EQ(1U, manager()->local_tab_pool_.Capacity());
1334 EXPECT_TRUE(manager()->local_tab_pool_.Empty());
1336 // Verify TabLinks.
1337 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
1338 ASSERT_EQ(1U, tab_map.size());
1339 int tab_id = out[2].sync_data().GetSpecifics().session().tab().tab_id();
1340 EXPECT_EQ(tab_node_id, tab_map.find(tab_id)->second->tab_node_id());
1343 // Test that receiving a session delete from sync removes the session
1344 // from tracking.
1345 TEST_F(SessionsSyncManagerTest, ProcessForeignDelete) {
1346 InitWithNoSyncData();
1347 SessionID::id_type n[] = {5};
1348 std::vector<sync_pb::SessionSpecifics> tabs1;
1349 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
1350 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1351 "tag1", tab_list, &tabs1));
1353 syncer::SyncChangeList changes;
1354 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1355 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1356 manager()->ProcessSyncChanges(FROM_HERE, changes);
1358 std::vector<const SyncedSession*> foreign_sessions;
1359 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1360 ASSERT_EQ(1U, foreign_sessions.size());
1362 changes.clear();
1363 foreign_sessions.clear();
1364 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE));
1365 manager()->ProcessSyncChanges(FROM_HERE, changes);
1367 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1370 // TODO(shashishekhar): "Move this to TabNodePool unittests."
1371 TEST_F(SessionsSyncManagerTest, SaveUnassociatedNodesForReassociation) {
1372 syncer::SyncChangeList changes;
1373 InitWithNoSyncData();
1375 std::string local_tag = manager()->current_machine_tag();
1376 // Create a free node and then dissassociate sessions so that it ends up
1377 // unassociated.
1378 manager()->local_tab_pool_.GetFreeTabNode(&changes);
1380 // Update the tab_id of the node, so that it is considered a valid
1381 // unassociated node otherwise it will be mistaken for a corrupted node and
1382 // will be deleted before being added to the tab node pool.
1383 sync_pb::EntitySpecifics entity(changes[0].sync_data().GetSpecifics());
1384 entity.mutable_session()->mutable_tab()->set_tab_id(1);
1385 SyncData d(SyncData::CreateRemoteData(
1387 entity,
1388 base::Time(),
1389 syncer::AttachmentIdList(),
1390 syncer::AttachmentServiceProxyForTest::Create()));
1391 syncer::SyncDataList in(&d, &d + 1);
1392 changes.clear();
1393 SessionsSyncManager manager2(profile(), local_device(), NewDummyRouter());
1394 syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
1395 syncer::SESSIONS, in,
1396 scoped_ptr<syncer::SyncChangeProcessor>(
1397 new TestSyncProcessorStub(&changes)),
1398 scoped_ptr<syncer::SyncErrorFactory>(
1399 new syncer::SyncErrorFactoryMock()));
1400 ASSERT_FALSE(result.error().IsSet());
1401 EXPECT_TRUE(FilterOutLocalHeaderChanges(&changes)->empty());
1404 TEST_F(SessionsSyncManagerTest, MergeDeletesCorruptNode) {
1405 syncer::SyncChangeList changes;
1406 InitWithNoSyncData();
1408 std::string local_tag = manager()->current_machine_tag();
1409 int tab_node_id = manager()->local_tab_pool_.GetFreeTabNode(&changes);
1410 SyncData d(SyncData::CreateRemoteData(
1412 changes[0].sync_data().GetSpecifics(),
1413 base::Time(),
1414 syncer::AttachmentIdList(),
1415 syncer::AttachmentServiceProxyForTest::Create()));
1416 syncer::SyncDataList in(&d, &d + 1);
1417 changes.clear();
1418 TearDown();
1419 SetUp();
1420 InitWithSyncDataTakeOutput(in, &changes);
1421 EXPECT_EQ(1U, FilterOutLocalHeaderChanges(&changes)->size());
1422 EXPECT_EQ(SyncChange::ACTION_DELETE, changes[0].change_type());
1423 EXPECT_EQ(TabNodePool::TabIdToTag(local_tag, tab_node_id),
1424 syncer::SyncDataLocal(changes[0].sync_data()).GetTag());
1427 // Test that things work if a tab is initially ignored.
1428 TEST_F(SessionsSyncManagerTest, AssociateWindowsDontReloadTabs) {
1429 syncer::SyncChangeList out;
1430 // Go to a URL that is ignored by session syncing.
1431 AddTab(browser(), GURL("chrome://preferences/"));
1432 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1433 ASSERT_EQ(2U, out.size()); // Header add and update.
1434 EXPECT_EQ(
1436 out[1].sync_data().GetSpecifics().session().header().window_size());
1437 out.clear();
1439 // Go to a sync-interesting URL.
1440 NavigateAndCommitActiveTab(GURL("http://foo2"));
1442 EXPECT_EQ(3U, out.size()); // Tab add, update, and header update.
1444 EXPECT_TRUE(
1445 base::StartsWith(syncer::SyncDataLocal(out[0].sync_data()).GetTag(),
1446 manager()->current_machine_tag(),
1447 base::CompareCase::SENSITIVE));
1448 EXPECT_EQ(manager()->current_machine_tag(),
1449 out[0].sync_data().GetSpecifics().session().session_tag());
1450 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
1452 EXPECT_TRUE(
1453 base::StartsWith(syncer::SyncDataLocal(out[1].sync_data()).GetTag(),
1454 manager()->current_machine_tag(),
1455 base::CompareCase::SENSITIVE));
1456 EXPECT_EQ(manager()->current_machine_tag(),
1457 out[1].sync_data().GetSpecifics().session().session_tag());
1458 EXPECT_TRUE(out[1].sync_data().GetSpecifics().session().has_tab());
1459 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
1461 EXPECT_TRUE(out[2].IsValid());
1462 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
1463 const SyncData data(out[2].sync_data());
1464 EXPECT_EQ(manager()->current_machine_tag(),
1465 syncer::SyncDataLocal(data).GetTag());
1466 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1467 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1468 EXPECT_TRUE(specifics.has_header());
1469 const sync_pb::SessionHeader& header_s = specifics.header();
1470 EXPECT_EQ(1, header_s.window_size());
1471 EXPECT_EQ(1, header_s.window(0).tab_size());
1474 // Tests that the SyncSessionManager responds to local tab events properly.
1475 TEST_F(SessionsSyncManagerTest, OnLocalTabModified) {
1476 syncer::SyncChangeList out;
1477 // Init with no local data, relies on MergeLocalSessionNoTabs.
1478 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1479 ASSERT_FALSE(manager()->current_machine_tag().empty());
1480 ASSERT_EQ(2U, out.size());
1482 // Copy the original header.
1483 sync_pb::EntitySpecifics header(out[0].sync_data().GetSpecifics());
1484 out.clear();
1486 const GURL foo1("http://foo/1");
1487 const GURL foo2("http://foo/2");
1488 const GURL bar1("http://bar/1");
1489 const GURL bar2("http://bar/2");
1490 AddTab(browser(), foo1);
1491 NavigateAndCommitActiveTab(foo2);
1492 AddTab(browser(), bar1);
1493 NavigateAndCommitActiveTab(bar2);
1495 // One add, one update for each AddTab.
1496 // One update for each NavigateAndCommit.
1497 // = 6 total tab updates.
1498 // One header update corresponding to each of those.
1499 // = 6 total header updates.
1500 // 12 total updates.
1501 ASSERT_EQ(12U, out.size());
1503 // Verify the tab node creations and updates to ensure the SyncProcessor
1504 // sees the right operations.
1505 for (int i = 0; i < 12; i++) {
1506 SCOPED_TRACE(i);
1507 EXPECT_TRUE(out[i].IsValid());
1508 const SyncData data(out[i].sync_data());
1509 EXPECT_TRUE(base::StartsWith(syncer::SyncDataLocal(data).GetTag(),
1510 manager()->current_machine_tag(),
1511 base::CompareCase::SENSITIVE));
1512 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1513 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1514 if (i % 6 == 0) {
1515 // First thing on an AddTab is a no-op header update for parented tab.
1516 EXPECT_EQ(header.SerializeAsString(),
1517 data.GetSpecifics().SerializeAsString());
1518 EXPECT_EQ(manager()->current_machine_tag(),
1519 syncer::SyncDataLocal(data).GetTag());
1520 } else if (i % 6 == 1) {
1521 // Next, the TabNodePool should create the tab node.
1522 EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
1523 EXPECT_EQ(TabNodePool::TabIdToTag(
1524 manager()->current_machine_tag(),
1525 data.GetSpecifics().session().tab_node_id()),
1526 syncer::SyncDataLocal(data).GetTag());
1527 } else if (i % 6 == 2) {
1528 // Then we see the tab update to the URL.
1529 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1530 EXPECT_EQ(TabNodePool::TabIdToTag(
1531 manager()->current_machine_tag(),
1532 data.GetSpecifics().session().tab_node_id()),
1533 syncer::SyncDataLocal(data).GetTag());
1534 ASSERT_TRUE(specifics.has_tab());
1535 } else if (i % 6 == 3) {
1536 // The header needs to be updated to reflect the new window state.
1537 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1538 EXPECT_TRUE(specifics.has_header());
1539 } else if (i % 6 == 4) {
1540 // Now we move on to NavigateAndCommit. Update the tab.
1541 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1542 EXPECT_EQ(TabNodePool::TabIdToTag(
1543 manager()->current_machine_tag(),
1544 data.GetSpecifics().session().tab_node_id()),
1545 syncer::SyncDataLocal(data).GetTag());
1546 ASSERT_TRUE(specifics.has_tab());
1547 } else if (i % 6 == 5) {
1548 // The header needs to be updated to reflect the new window state.
1549 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1550 ASSERT_TRUE(specifics.has_header());
1551 header = data.GetSpecifics();
1555 // Verify the actual content to ensure sync sees the right data.
1556 // When it's all said and done, the header should reflect two tabs.
1557 const sync_pb::SessionHeader& session_header = header.session().header();
1558 ASSERT_EQ(1, session_header.window_size());
1559 EXPECT_EQ(2, session_header.window(0).tab_size());
1561 // ASSERT_TRUEs above allow us to dive in freely here.
1562 // Verify first tab.
1563 const sync_pb::SessionTab& tab1_1 =
1564 out[2].sync_data().GetSpecifics().session().tab();
1565 ASSERT_EQ(1, tab1_1.navigation_size());
1566 EXPECT_EQ(foo1.spec(), tab1_1.navigation(0).virtual_url());
1567 const sync_pb::SessionTab& tab1_2 =
1568 out[4].sync_data().GetSpecifics().session().tab();
1569 ASSERT_EQ(2, tab1_2.navigation_size());
1570 EXPECT_EQ(foo1.spec(), tab1_2.navigation(0).virtual_url());
1571 EXPECT_EQ(foo2.spec(), tab1_2.navigation(1).virtual_url());
1573 // Verify second tab.
1574 const sync_pb::SessionTab& tab2_1 =
1575 out[8].sync_data().GetSpecifics().session().tab();
1576 ASSERT_EQ(1, tab2_1.navigation_size());
1577 EXPECT_EQ(bar1.spec(), tab2_1.navigation(0).virtual_url());
1578 const sync_pb::SessionTab& tab2_2 =
1579 out[10].sync_data().GetSpecifics().session().tab();
1580 ASSERT_EQ(2, tab2_2.navigation_size());
1581 EXPECT_EQ(bar1.spec(), tab2_2.navigation(0).virtual_url());
1582 EXPECT_EQ(bar2.spec(), tab2_2.navigation(1).virtual_url());
1585 // Ensure model association associates the pre-existing tabs.
1586 TEST_F(SessionsSyncManagerTest, MergeLocalSessionExistingTabs) {
1587 AddTab(browser(), GURL("http://foo1"));
1588 NavigateAndCommitActiveTab(GURL("http://foo2")); // Adds back entry.
1589 AddTab(browser(), GURL("http://bar1"));
1590 NavigateAndCommitActiveTab(GURL("http://bar2")); // Adds back entry.
1592 syncer::SyncChangeList out;
1593 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1594 ASSERT_EQ(6U, out.size());
1596 // Check that this machine's data is not included in the foreign windows.
1597 std::vector<const SyncedSession*> foreign_sessions;
1598 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1600 // Verify the header.
1601 EXPECT_TRUE(out[0].IsValid());
1602 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
1603 const SyncData data(out[0].sync_data());
1604 EXPECT_EQ(manager()->current_machine_tag(),
1605 syncer::SyncDataLocal(data).GetTag());
1606 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1607 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1608 EXPECT_TRUE(specifics.has_header());
1609 const sync_pb::SessionHeader& header_s = specifics.header();
1610 EXPECT_TRUE(header_s.has_device_type());
1611 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
1612 EXPECT_EQ(0, header_s.window_size());
1614 // Verify the tab node creations and updates with content.
1615 for (int i = 1; i < 5; i++) {
1616 EXPECT_TRUE(out[i].IsValid());
1617 const SyncData data(out[i].sync_data());
1618 EXPECT_TRUE(base::StartsWith(syncer::SyncDataLocal(data).GetTag(),
1619 manager()->current_machine_tag(),
1620 base::CompareCase::SENSITIVE));
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 // Verify that GetAllForeignSessions returns all sessions sorted by recency.
2093 TEST_F(SessionsSyncManagerTest, GetAllForeignSessions) {
2094 SessionID::id_type ids[] = {5, 10, 13, 17};
2095 std::vector<SessionID::id_type> tab_list(ids, ids + arraysize(ids));
2097 const std::string kTag = "tag1";
2098 std::vector<sync_pb::SessionSpecifics> tabs1;
2099 sync_pb::SessionSpecifics meta1(helper()->BuildForeignSession(
2100 kTag, tab_list, &tabs1));
2102 const std::string kTag2 = "tag2";
2103 std::vector<sync_pb::SessionSpecifics> tabs2;
2104 sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession(
2105 kTag2, tab_list, &tabs2));
2107 sync_pb::EntitySpecifics entity1;
2108 entity1.mutable_session()->CopyFrom(meta1);
2109 sync_pb::EntitySpecifics entity2;
2110 entity2.mutable_session()->CopyFrom(meta2);
2112 syncer::SyncDataList initial_data;
2113 initial_data.push_back(SyncData::CreateRemoteData(
2115 entity1,
2116 base::Time::FromInternalValue(10),
2117 syncer::AttachmentIdList(),
2118 syncer::AttachmentServiceProxyForTest::Create()));
2119 AddTabsToSyncDataList(tabs1, &initial_data);
2120 initial_data.push_back(SyncData::CreateRemoteData(
2122 entity2,
2123 base::Time::FromInternalValue(200),
2124 syncer::AttachmentIdList(),
2125 syncer::AttachmentServiceProxyForTest::Create()));
2126 AddTabsToSyncDataList(tabs2, &initial_data);
2128 syncer::SyncChangeList output;
2129 InitWithSyncDataTakeOutput(initial_data, &output);
2131 std::vector<const SyncedSession*> foreign_sessions;
2132 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
2133 ASSERT_EQ(2U, foreign_sessions.size());
2134 ASSERT_GT(foreign_sessions[0]->modified_time,
2135 foreign_sessions[1]->modified_time);
2138 // Verify that GetForeignSessionTabs returns all tabs for a session sorted
2139 // by recency.
2140 TEST_F(SessionsSyncManagerTest, GetForeignSessionTabs) {
2141 const std::string kTag = "tag1";
2143 SessionID::id_type n1[] = {5, 10, 13, 17};
2144 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
2145 std::vector<sync_pb::SessionSpecifics> tabs1;
2146 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
2147 kTag, tab_list1, &tabs1));
2148 // Add a second window.
2149 SessionID::id_type n2[] = {7, 15, 18, 20};
2150 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
2151 helper()->AddWindowSpecifics(1, tab_list2, &meta);
2153 // Set up initial data.
2154 syncer::SyncDataList initial_data;
2155 sync_pb::EntitySpecifics entity;
2156 entity.mutable_session()->CopyFrom(meta);
2157 initial_data.push_back(SyncData::CreateRemoteData(
2159 entity,
2160 base::Time(),
2161 syncer::AttachmentIdList(),
2162 syncer::AttachmentServiceProxyForTest::Create()));
2164 // Add the first window's tabs.
2165 AddTabsToSyncDataList(tabs1, &initial_data);
2167 // Add the second window's tabs.
2168 for (size_t i = 0; i < tab_list2.size(); ++i) {
2169 sync_pb::EntitySpecifics entity;
2170 helper()->BuildTabSpecifics(kTag, 0, tab_list2[i],
2171 entity.mutable_session());
2172 // Order the tabs oldest to most ReceiveDuplicateUnassociatedTabs and
2173 // left to right visually.
2174 initial_data.push_back(SyncData::CreateRemoteData(
2175 i + 10,
2176 entity,
2177 base::Time::FromInternalValue(i + 1),
2178 syncer::AttachmentIdList(),
2179 syncer::AttachmentServiceProxyForTest::Create()));
2182 syncer::SyncChangeList output;
2183 InitWithSyncDataTakeOutput(initial_data, &output);
2185 std::vector<const sessions::SessionTab*> tabs;
2186 ASSERT_TRUE(manager()->GetForeignSessionTabs(kTag, &tabs));
2187 // Assert that the size matches the total number of tabs and that the order
2188 // is from most recent to least.
2189 ASSERT_EQ(tab_list1.size() + tab_list2.size(), tabs.size());
2190 base::Time last_time;
2191 for (size_t i = 0; i < tabs.size(); ++i) {
2192 base::Time this_time = tabs[i]->timestamp;
2193 if (i > 0)
2194 ASSERT_GE(last_time, this_time);
2195 last_time = tabs[i]->timestamp;
2199 } // namespace browser_sync