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