1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/sync/sessions/sessions_sync_manager.h"
7 #include "base/strings/string_util.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/sessions/session_tab_helper.h"
10 #include "chrome/browser/sync/glue/session_sync_test_helper.h"
11 #include "chrome/browser/sync/glue/synced_tab_delegate.h"
12 #include "chrome/browser/sync/glue/synced_window_delegate.h"
13 #include "chrome/browser/sync/sessions/notification_service_sessions_router.h"
14 #include "chrome/browser/sync/sessions/sessions_util.h"
15 #include "chrome/browser/sync/sessions/synced_window_delegates_getter.h"
16 #include "chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/test/base/browser_with_test_window_test.h"
19 #include "components/sessions/serialized_navigation_entry_test_helper.h"
20 #include "components/sessions/session_id.h"
21 #include "components/sessions/session_types.h"
22 #include "components/sync_driver/device_info.h"
23 #include "components/sync_driver/local_device_info_provider_mock.h"
24 #include "content/public/browser/navigation_entry.h"
25 #include "content/public/browser/notification_details.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/notification_source.h"
28 #include "content/public/browser/web_contents.h"
29 #include "sync/api/attachments/attachment_id.h"
30 #include "sync/api/sync_error_factory_mock.h"
31 #include "sync/internal_api/public/attachments/attachment_service_proxy_for_test.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 #include "testing/gtest/include/gtest/gtest.h"
35 using content::WebContents
;
36 using sessions::SerializedNavigationEntry
;
37 using sessions::SerializedNavigationEntryTestHelper
;
38 using sync_driver::DeviceInfo
;
39 using sync_driver::LocalDeviceInfoProvider
;
40 using sync_driver::LocalDeviceInfoProviderMock
;
41 using syncer::SyncChange
;
42 using syncer::SyncData
;
44 namespace browser_sync
{
48 class SyncedWindowDelegateOverride
: public SyncedWindowDelegate
{
50 explicit SyncedWindowDelegateOverride(const SyncedWindowDelegate
* 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(); }
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
{
109 TestSyncedWindowDelegatesGetter(
110 const std::set
<const SyncedWindowDelegate
*>& delegates
)
111 : delegates_(delegates
) {}
113 std::set
<const SyncedWindowDelegate
*> GetSyncedWindowDelegates() override
{
117 const std::set
<const SyncedWindowDelegate
*> delegates_
;
120 class TestSyncProcessorStub
: public syncer::SyncChangeProcessor
{
122 explicit TestSyncProcessorStub(syncer::SyncChangeList
* 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();
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
) {
147 void SetSyncDataToReturn(const syncer::SyncDataList
& data
) {
148 sync_data_to_return_
= data
;
152 syncer::SyncError error_
;
153 syncer::SyncChangeList
* output_
;
154 syncer::SyncDataList sync_data_to_return_
;
157 syncer::SyncChange
MakeRemoteChange(
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(
166 syncer::SyncData::CreateRemoteData(
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(
186 syncer::SyncData::CreateRemoteData(
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(
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
,
218 // Access the trial to activate it.
219 base::FieldTrialList::FindFullName(trial_name
);
222 class DummyRouter
: public LocalSessionEventRouter
{
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());
235 class SessionsSyncManagerTest
236 : public BrowserWithTestWindowTest
{
238 SessionsSyncManagerTest()
239 : test_processor_(NULL
) {
240 local_device_
.reset(new LocalDeviceInfoProviderMock(
242 "Wayne Gretzky's Hacking Box",
245 sync_pb::SyncEnums_DeviceType_TYPE_LINUX
,
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
;
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",
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();
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
);
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
;
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());
356 manager()->session_tracker_
.num_synced_tabs(std::string("tag")));
361 class SyncedTabDelegateFake
: public SyncedTabDelegate
{
363 SyncedTabDelegateFake() : current_entry_index_(0),
364 pending_entry_index_(-1),
365 is_supervised_(false),
367 blocked_navigations_(NULL
) {}
368 ~SyncedTabDelegateFake() override
{}
370 int GetCurrentEntryIndex() const override
{ return current_entry_index_
; }
371 void set_current_entry_index(int i
) {
372 current_entry_index_
= i
;
375 content::NavigationEntry
* GetEntryAtIndex(int i
) const override
{
376 const int size
= entries_
.size();
377 return (size
< i
+ 1) ? NULL
: entries_
[i
];
380 void AppendEntry(content::NavigationEntry
* entry
) {
381 entries_
.push_back(entry
);
384 int GetEntryCount() const override
{ return entries_
.size(); }
386 int GetPendingEntryIndex() const override
{ return pending_entry_index_
; }
387 void set_pending_entry_index(int i
) {
388 pending_entry_index_
= i
;
391 SessionID::id_type
GetWindowId() const override
{
392 return SessionID::id_type();
395 SessionID::id_type
GetSessionId() const override
{
396 return SessionID::id_type();
399 bool IsBeingDestroyed() const override
{ return false; }
400 Profile
* profile() const override
{ return NULL
; }
401 std::string
GetExtensionAppId() const override
{ return std::string(); }
402 content::NavigationEntry
* GetPendingEntry() const override
{ return NULL
; }
403 content::NavigationEntry
* GetActiveEntry() const override
{ return NULL
; }
404 bool ProfileIsSupervised() const override
{ return is_supervised_
; }
405 void set_is_supervised(bool is_supervised
) { is_supervised_
= is_supervised
; }
406 const std::vector
<const content::NavigationEntry
*>* GetBlockedNavigations()
408 return blocked_navigations_
;
410 void set_blocked_navigations(
411 std::vector
<const content::NavigationEntry
*>* navs
) {
412 blocked_navigations_
= navs
;
414 bool IsPinned() const override
{ return false; }
415 bool HasWebContents() const override
{ return false; }
416 content::WebContents
* GetWebContents() const override
{ return NULL
; }
418 // Session sync related methods.
419 int GetSyncId() const override
{ return sync_id_
; }
420 void SetSyncId(int sync_id
) override
{ sync_id_
= sync_id
; }
422 bool ShouldSync() const override
{
423 return sessions_util::ShouldSyncTab(*this);
427 current_entry_index_
= 0;
428 pending_entry_index_
= -1;
434 int current_entry_index_
;
435 int pending_entry_index_
;
438 std::vector
<const content::NavigationEntry
*>* blocked_navigations_
;
439 ScopedVector
<content::NavigationEntry
> entries_
;
444 // Test that we exclude tabs with only chrome:// and file:// schemed navigations
445 // from ShouldSyncTab(..).
446 TEST_F(SessionsSyncManagerTest
, ValidTabs
) {
447 SyncedTabDelegateFake tab
;
449 // A null entry shouldn't crash.
450 tab
.AppendEntry(NULL
);
451 EXPECT_FALSE(tab
.ShouldSync());
454 // A chrome:// entry isn't valid.
455 content::NavigationEntry
* entry(content::NavigationEntry::Create());
456 entry
->SetVirtualURL(GURL("chrome://preferences/"));
457 tab
.AppendEntry(entry
);
458 EXPECT_FALSE(tab
.ShouldSync());
461 // A file:// entry isn't valid, even in addition to another entry.
462 content::NavigationEntry
* entry2(content::NavigationEntry::Create());
463 entry2
->SetVirtualURL(GURL("file://bla"));
464 tab
.AppendEntry(entry2
);
465 EXPECT_FALSE(tab
.ShouldSync());
467 // Add a valid scheme entry to tab, making the tab valid.
468 content::NavigationEntry
* entry3(content::NavigationEntry::Create());
469 entry3
->SetVirtualURL(GURL("http://www.google.com"));
470 tab
.AppendEntry(entry3
);
471 EXPECT_FALSE(tab
.ShouldSync());
474 // Make sure GetCurrentVirtualURL() returns the virtual URL of the pending
475 // entry if the current entry is pending.
476 TEST_F(SessionsSyncManagerTest
, GetCurrentVirtualURLPending
) {
477 SyncedTabDelegateFake tab
;
478 content::NavigationEntry
* entry(content::NavigationEntry::Create());
479 entry
->SetVirtualURL(GURL("http://www.google.com"));
480 tab
.AppendEntry(entry
);
481 EXPECT_EQ(entry
->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab
));
484 // Make sure GetCurrentVirtualURL() returns the virtual URL of the current
485 // entry if the current entry is non-pending.
486 TEST_F(SessionsSyncManagerTest
, GetCurrentVirtualURLNonPending
) {
487 SyncedTabDelegateFake tab
;
488 content::NavigationEntry
* entry(content::NavigationEntry::Create());
489 entry
->SetVirtualURL(GURL("http://www.google.com"));
490 tab
.AppendEntry(entry
);
491 EXPECT_EQ(entry
->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab
));
494 static const base::Time kTime0
= base::Time::FromInternalValue(100);
495 static const base::Time kTime1
= base::Time::FromInternalValue(110);
496 static const base::Time kTime2
= base::Time::FromInternalValue(120);
497 static const base::Time kTime3
= base::Time::FromInternalValue(130);
498 static const base::Time kTime4
= base::Time::FromInternalValue(140);
499 static const base::Time kTime5
= base::Time::FromInternalValue(150);
500 static const base::Time kTime6
= base::Time::FromInternalValue(160);
501 static const base::Time kTime7
= base::Time::FromInternalValue(170);
502 static const base::Time kTime8
= base::Time::FromInternalValue(180);
503 static const base::Time kTime9
= base::Time::FromInternalValue(190);
505 // Populate the mock tab delegate with some data and navigation
506 // entries and make sure that setting a SessionTab from it preserves
507 // those entries (and clobbers any existing data).
508 TEST_F(SessionsSyncManagerTest
, SetSessionTabFromDelegate
) {
509 // Create a tab with three valid entries.
510 SyncedTabDelegateFake tab
;
511 content::NavigationEntry
* entry1(content::NavigationEntry::Create());
512 entry1
->SetVirtualURL(GURL("http://www.google.com"));
513 entry1
->SetTimestamp(kTime1
);
514 entry1
->SetHttpStatusCode(200);
515 content::NavigationEntry
* entry2(content::NavigationEntry::Create());
516 entry2
->SetVirtualURL(GURL("http://www.noodle.com"));
517 entry2
->SetTimestamp(kTime2
);
518 entry2
->SetHttpStatusCode(201);
519 content::NavigationEntry
* entry3(content::NavigationEntry::Create());
520 entry3
->SetVirtualURL(GURL("http://www.doodle.com"));
521 entry3
->SetTimestamp(kTime3
);
522 entry3
->SetHttpStatusCode(202);
524 tab
.AppendEntry(entry1
);
525 tab
.AppendEntry(entry2
);
526 tab
.AppendEntry(entry3
);
527 tab
.set_current_entry_index(2);
529 sessions::SessionTab session_tab
;
530 session_tab
.window_id
.set_id(1);
531 session_tab
.tab_id
.set_id(1);
532 session_tab
.tab_visual_index
= 1;
533 session_tab
.current_navigation_index
= 1;
534 session_tab
.pinned
= true;
535 session_tab
.extension_app_id
= "app id";
536 session_tab
.user_agent_override
= "override";
537 session_tab
.timestamp
= kTime5
;
538 session_tab
.navigations
.push_back(
539 SerializedNavigationEntryTestHelper::CreateNavigation(
540 "http://www.example.com", "Example"));
541 session_tab
.session_storage_persistent_id
= "persistent id";
542 manager()->SetSessionTabFromDelegate(tab
, kTime4
, &session_tab
);
544 EXPECT_EQ(0, session_tab
.window_id
.id());
545 EXPECT_EQ(0, session_tab
.tab_id
.id());
546 EXPECT_EQ(0, session_tab
.tab_visual_index
);
547 EXPECT_EQ(2, session_tab
.current_navigation_index
);
548 EXPECT_FALSE(session_tab
.pinned
);
549 EXPECT_TRUE(session_tab
.extension_app_id
.empty());
550 EXPECT_TRUE(session_tab
.user_agent_override
.empty());
551 EXPECT_EQ(kTime4
, session_tab
.timestamp
);
552 ASSERT_EQ(3u, session_tab
.navigations
.size());
553 EXPECT_EQ(entry1
->GetVirtualURL(),
554 session_tab
.navigations
[0].virtual_url());
555 EXPECT_EQ(entry2
->GetVirtualURL(),
556 session_tab
.navigations
[1].virtual_url());
557 EXPECT_EQ(entry3
->GetVirtualURL(),
558 session_tab
.navigations
[2].virtual_url());
559 EXPECT_EQ(kTime1
, session_tab
.navigations
[0].timestamp());
560 EXPECT_EQ(kTime2
, session_tab
.navigations
[1].timestamp());
561 EXPECT_EQ(kTime3
, session_tab
.navigations
[2].timestamp());
562 EXPECT_EQ(200, session_tab
.navigations
[0].http_status_code());
563 EXPECT_EQ(201, session_tab
.navigations
[1].http_status_code());
564 EXPECT_EQ(202, session_tab
.navigations
[2].http_status_code());
565 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID
,
566 session_tab
.navigations
[0].blocked_state());
567 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID
,
568 session_tab
.navigations
[1].blocked_state());
569 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID
,
570 session_tab
.navigations
[2].blocked_state());
571 EXPECT_TRUE(session_tab
.session_storage_persistent_id
.empty());
574 // Ensure the current_navigation_index gets set properly when the navigation
575 // stack gets trucated to +/- 6 entries.
576 TEST_F(SessionsSyncManagerTest
, SetSessionTabFromDelegateNavigationIndex
) {
577 SyncedTabDelegateFake tab
;
578 content::NavigationEntry
* entry0(content::NavigationEntry::Create());
579 entry0
->SetVirtualURL(GURL("http://www.google.com"));
580 entry0
->SetTimestamp(kTime0
);
581 entry0
->SetHttpStatusCode(200);
582 content::NavigationEntry
* entry1(content::NavigationEntry::Create());
583 entry1
->SetVirtualURL(GURL("http://www.zoogle.com"));
584 entry1
->SetTimestamp(kTime1
);
585 entry1
->SetHttpStatusCode(200);
586 content::NavigationEntry
* entry2(content::NavigationEntry::Create());
587 entry2
->SetVirtualURL(GURL("http://www.noogle.com"));
588 entry2
->SetTimestamp(kTime2
);
589 entry2
->SetHttpStatusCode(200);
590 content::NavigationEntry
* entry3(content::NavigationEntry::Create());
591 entry3
->SetVirtualURL(GURL("http://www.doogle.com"));
592 entry3
->SetTimestamp(kTime3
);
593 entry3
->SetHttpStatusCode(200);
594 content::NavigationEntry
* entry4(content::NavigationEntry::Create());
595 entry4
->SetVirtualURL(GURL("http://www.yoogle.com"));
596 entry4
->SetTimestamp(kTime4
);
597 entry4
->SetHttpStatusCode(200);
598 content::NavigationEntry
* entry5(content::NavigationEntry::Create());
599 entry5
->SetVirtualURL(GURL("http://www.foogle.com"));
600 entry5
->SetTimestamp(kTime5
);
601 entry5
->SetHttpStatusCode(200);
602 content::NavigationEntry
* entry6(content::NavigationEntry::Create());
603 entry6
->SetVirtualURL(GURL("http://www.boogle.com"));
604 entry6
->SetTimestamp(kTime6
);
605 entry6
->SetHttpStatusCode(200);
606 content::NavigationEntry
* entry7(content::NavigationEntry::Create());
607 entry7
->SetVirtualURL(GURL("http://www.moogle.com"));
608 entry7
->SetTimestamp(kTime7
);
609 entry7
->SetHttpStatusCode(200);
610 content::NavigationEntry
* entry8(content::NavigationEntry::Create());
611 entry8
->SetVirtualURL(GURL("http://www.poogle.com"));
612 entry8
->SetTimestamp(kTime8
);
613 entry8
->SetHttpStatusCode(200);
614 content::NavigationEntry
* entry9(content::NavigationEntry::Create());
615 entry9
->SetVirtualURL(GURL("http://www.roogle.com"));
616 entry9
->SetTimestamp(kTime9
);
617 entry9
->SetHttpStatusCode(200);
619 tab
.AppendEntry(entry0
);
620 tab
.AppendEntry(entry1
);
621 tab
.AppendEntry(entry2
);
622 tab
.AppendEntry(entry3
);
623 tab
.AppendEntry(entry4
);
624 tab
.AppendEntry(entry5
);
625 tab
.AppendEntry(entry6
);
626 tab
.AppendEntry(entry7
);
627 tab
.AppendEntry(entry8
);
628 tab
.AppendEntry(entry9
);
629 tab
.set_current_entry_index(8);
631 sessions::SessionTab session_tab
;
632 manager()->SetSessionTabFromDelegate(tab
, kTime9
, &session_tab
);
634 EXPECT_EQ(6, session_tab
.current_navigation_index
);
635 ASSERT_EQ(8u, session_tab
.navigations
.size());
636 EXPECT_EQ(entry2
->GetVirtualURL(),
637 session_tab
.navigations
[0].virtual_url());
638 EXPECT_EQ(entry3
->GetVirtualURL(),
639 session_tab
.navigations
[1].virtual_url());
640 EXPECT_EQ(entry4
->GetVirtualURL(),
641 session_tab
.navigations
[2].virtual_url());
644 // Ensure the current_navigation_index gets set to the end of the navigation
645 // stack if the current navigation is invalid.
646 TEST_F(SessionsSyncManagerTest
, SetSessionTabFromDelegateCurrentInvalid
) {
647 SyncedTabDelegateFake tab
;
648 content::NavigationEntry
* entry0(content::NavigationEntry::Create());
649 entry0
->SetVirtualURL(GURL("http://www.google.com"));
650 entry0
->SetTimestamp(kTime0
);
651 entry0
->SetHttpStatusCode(200);
652 content::NavigationEntry
* entry1(content::NavigationEntry::Create());
653 entry1
->SetVirtualURL(GURL(""));
654 entry1
->SetTimestamp(kTime1
);
655 entry1
->SetHttpStatusCode(200);
656 content::NavigationEntry
* entry2(content::NavigationEntry::Create());
657 entry2
->SetVirtualURL(GURL("http://www.noogle.com"));
658 entry2
->SetTimestamp(kTime2
);
659 entry2
->SetHttpStatusCode(200);
660 content::NavigationEntry
* entry3(content::NavigationEntry::Create());
661 entry3
->SetVirtualURL(GURL("http://www.doogle.com"));
662 entry3
->SetTimestamp(kTime3
);
663 entry3
->SetHttpStatusCode(200);
665 tab
.AppendEntry(entry0
);
666 tab
.AppendEntry(entry1
);
667 tab
.AppendEntry(entry2
);
668 tab
.AppendEntry(entry3
);
669 tab
.set_current_entry_index(1);
671 sessions::SessionTab session_tab
;
672 manager()->SetSessionTabFromDelegate(tab
, kTime9
, &session_tab
);
674 EXPECT_EQ(2, session_tab
.current_navigation_index
);
675 ASSERT_EQ(3u, session_tab
.navigations
.size());
678 // Tests that variation ids are set correctly.
679 TEST_F(SessionsSyncManagerTest
, SetVariationIds
) {
680 // Create two trials with a group which has a variation id for Chrome Sync
681 // and one with a variation id for another service.
682 const variations::VariationID kVariationId1
= 3300200;
683 const variations::VariationID kVariationId2
= 3300300;
684 const variations::VariationID kVariationId3
= 3300400;
686 base::FieldTrialList
field_trial_list(NULL
);
687 CreateAndActivateFieldTrial("trial name 1", "group name", kVariationId1
,
688 variations::CHROME_SYNC_SERVICE
);
689 CreateAndActivateFieldTrial("trial name 2", "group name", kVariationId2
,
690 variations::CHROME_SYNC_SERVICE
);
691 CreateAndActivateFieldTrial("trial name 3", "group name", kVariationId3
,
692 variations::GOOGLE_UPDATE_SERVICE
);
694 sessions::SessionTab session_tab
;
695 manager()->SetVariationIds(&session_tab
);
697 ASSERT_EQ(2u, session_tab
.variation_ids
.size());
698 EXPECT_EQ(kVariationId1
, session_tab
.variation_ids
[0]);
699 EXPECT_EQ(kVariationId2
, session_tab
.variation_ids
[1]);
702 // Tests that for supervised users blocked navigations are recorded and marked
703 // as such, while regular navigations are marked as allowed.
704 TEST_F(SessionsSyncManagerTest
, BlockedNavigations
) {
705 SyncedTabDelegateFake tab
;
706 content::NavigationEntry
* entry1(content::NavigationEntry::Create());
707 entry1
->SetVirtualURL(GURL("http://www.google.com"));
708 entry1
->SetTimestamp(kTime1
);
709 tab
.AppendEntry(entry1
);
711 content::NavigationEntry
* entry2
= content::NavigationEntry::Create();
712 entry2
->SetVirtualURL(GURL("http://blocked.com/foo"));
713 entry2
->SetTimestamp(kTime2
);
714 content::NavigationEntry
* entry3
= content::NavigationEntry::Create();
715 entry3
->SetVirtualURL(GURL("http://evil.com"));
716 entry3
->SetTimestamp(kTime3
);
717 ScopedVector
<const content::NavigationEntry
> blocked_navigations
;
718 blocked_navigations
.push_back(entry2
);
719 blocked_navigations
.push_back(entry3
);
721 tab
.set_is_supervised(true);
722 tab
.set_blocked_navigations(&blocked_navigations
.get());
724 sessions::SessionTab session_tab
;
725 session_tab
.window_id
.set_id(1);
726 session_tab
.tab_id
.set_id(1);
727 session_tab
.tab_visual_index
= 1;
728 session_tab
.current_navigation_index
= 1;
729 session_tab
.pinned
= true;
730 session_tab
.extension_app_id
= "app id";
731 session_tab
.user_agent_override
= "override";
732 session_tab
.timestamp
= kTime5
;
733 session_tab
.navigations
.push_back(
734 SerializedNavigationEntryTestHelper::CreateNavigation(
735 "http://www.example.com", "Example"));
736 session_tab
.session_storage_persistent_id
= "persistent id";
737 manager()->SetSessionTabFromDelegate(tab
, kTime4
, &session_tab
);
739 EXPECT_EQ(0, session_tab
.window_id
.id());
740 EXPECT_EQ(0, session_tab
.tab_id
.id());
741 EXPECT_EQ(0, session_tab
.tab_visual_index
);
742 EXPECT_EQ(0, session_tab
.current_navigation_index
);
743 EXPECT_FALSE(session_tab
.pinned
);
744 EXPECT_TRUE(session_tab
.extension_app_id
.empty());
745 EXPECT_TRUE(session_tab
.user_agent_override
.empty());
746 EXPECT_EQ(kTime4
, session_tab
.timestamp
);
747 ASSERT_EQ(3u, session_tab
.navigations
.size());
748 EXPECT_EQ(entry1
->GetVirtualURL(),
749 session_tab
.navigations
[0].virtual_url());
750 EXPECT_EQ(entry2
->GetVirtualURL(),
751 session_tab
.navigations
[1].virtual_url());
752 EXPECT_EQ(entry3
->GetVirtualURL(),
753 session_tab
.navigations
[2].virtual_url());
754 EXPECT_EQ(kTime1
, session_tab
.navigations
[0].timestamp());
755 EXPECT_EQ(kTime2
, session_tab
.navigations
[1].timestamp());
756 EXPECT_EQ(kTime3
, session_tab
.navigations
[2].timestamp());
757 EXPECT_EQ(SerializedNavigationEntry::STATE_ALLOWED
,
758 session_tab
.navigations
[0].blocked_state());
759 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED
,
760 session_tab
.navigations
[1].blocked_state());
761 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED
,
762 session_tab
.navigations
[2].blocked_state());
763 EXPECT_TRUE(session_tab
.session_storage_persistent_id
.empty());
766 // Tests that the local session header objects is created properly in
767 // presence of no other session activity, once and only once.
768 TEST_F(SessionsSyncManagerTest
, MergeLocalSessionNoTabs
) {
769 syncer::SyncChangeList out
;
770 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
771 EXPECT_FALSE(manager()->current_machine_tag().empty());
773 EXPECT_EQ(2U, out
.size());
774 EXPECT_TRUE(out
[0].IsValid());
775 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[0].change_type());
776 const SyncData
data(out
[0].sync_data());
777 EXPECT_EQ(manager()->current_machine_tag(),
778 syncer::SyncDataLocal(data
).GetTag());
779 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
780 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
781 EXPECT_TRUE(specifics
.has_header());
782 const sync_pb::SessionHeader
& header_s
= specifics
.header();
783 EXPECT_TRUE(header_s
.has_device_type());
784 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s
.client_name());
785 EXPECT_EQ(0, header_s
.window_size());
787 EXPECT_TRUE(out
[1].IsValid());
788 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[1].change_type());
789 const SyncData
data_2(out
[1].sync_data());
790 EXPECT_EQ(manager()->current_machine_tag(),
791 syncer::SyncDataLocal(data_2
).GetTag());
792 const sync_pb::SessionSpecifics
& specifics2(data_2
.GetSpecifics().session());
793 EXPECT_EQ(manager()->current_machine_tag(), specifics2
.session_tag());
794 EXPECT_TRUE(specifics2
.has_header());
795 const sync_pb::SessionHeader
& header_s2
= specifics2
.header();
796 EXPECT_EQ(0, header_s2
.window_size());
798 // Now take that header node and feed it in as input.
799 SyncData
d(SyncData::CreateRemoteData(
803 syncer::AttachmentIdList(),
804 syncer::AttachmentServiceProxyForTest::Create()));
805 syncer::SyncDataList
in(&d
, &d
+ 1);
807 SessionsSyncManager
manager2(profile(), local_device(), NewDummyRouter());
808 syncer::SyncMergeResult result
= manager2
.MergeDataAndStartSyncing(
809 syncer::SESSIONS
, in
,
810 scoped_ptr
<syncer::SyncChangeProcessor
>(
811 new TestSyncProcessorStub(&out
)),
812 scoped_ptr
<syncer::SyncErrorFactory
>(
813 new syncer::SyncErrorFactoryMock()));
814 ASSERT_FALSE(result
.error().IsSet());
816 EXPECT_EQ(1U, out
.size());
817 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[0].change_type());
818 EXPECT_TRUE(out
[0].sync_data().GetSpecifics().session().has_header());
821 // Ensure model association associates the pre-existing tabs.
822 TEST_F(SessionsSyncManagerTest
, SwappedOutOnRestore
) {
823 AddTab(browser(), GURL("http://foo1"));
824 NavigateAndCommitActiveTab(GURL("http://foo2"));
825 AddTab(browser(), GURL("http://bar1"));
826 NavigateAndCommitActiveTab(GURL("http://bar2"));
827 AddTab(browser(), GURL("http://baz1"));
828 NavigateAndCommitActiveTab(GURL("http://baz2"));
829 const int kRestoredTabId
= 1337;
830 const int kNewTabId
= 2468;
832 syncer::SyncDataList in
;
833 syncer::SyncChangeList out
;
834 InitWithSyncDataTakeOutput(in
, &out
);
836 // Should be one header add, 3 tab add/update pairs, one header update.
837 ASSERT_EQ(8U, out
.size());
839 // For input, we set up:
840 // * one "normal" fully loaded tab
841 // * one "frozen" tab with no WebContents and a tab_id change
842 // * one "frozen" tab with no WebContents and no tab_id change
843 SyncData
t0(SyncData::CreateRemoteData(
845 out
[2].sync_data().GetSpecifics(),
847 syncer::AttachmentIdList(),
848 syncer::AttachmentServiceProxyForTest::Create()));
849 sync_pb::EntitySpecifics
entity(out
[4].sync_data().GetSpecifics());
850 entity
.mutable_session()->mutable_tab()->set_tab_id(kRestoredTabId
);
851 SyncData
t1(SyncData::CreateRemoteData(
855 syncer::AttachmentIdList(),
856 syncer::AttachmentServiceProxyForTest::Create()));
857 SyncData
t2(SyncData::CreateRemoteData(
859 out
[6].sync_data().GetSpecifics(),
861 syncer::AttachmentIdList(),
862 syncer::AttachmentServiceProxyForTest::Create()));
867 manager()->StopSyncing(syncer::SESSIONS
);
869 const std::set
<const SyncedWindowDelegate
*>& windows
=
870 SyncedWindowDelegate::GetAll();
871 ASSERT_EQ(1U, windows
.size());
872 SyncedTabDelegateFake t1_override
, t2_override
;
873 t1_override
.SetSyncId(1); // No WebContents by default.
874 t2_override
.SetSyncId(2); // No WebContents by default.
875 SyncedWindowDelegateOverride
window_override(*windows
.begin());
876 window_override
.OverrideTabAt(1, &t1_override
, kNewTabId
);
877 window_override
.OverrideTabAt(2, &t2_override
,
878 t2
.GetSpecifics().session().tab().tab_id());
879 std::set
<const SyncedWindowDelegate
*> delegates
;
880 delegates
.insert(&window_override
);
881 scoped_ptr
<TestSyncedWindowDelegatesGetter
> getter(
882 new TestSyncedWindowDelegatesGetter(delegates
));
883 manager()->synced_window_getter_
.reset(getter
.release());
885 syncer::SyncMergeResult result
= manager()->MergeDataAndStartSyncing(
886 syncer::SESSIONS
, in
,
887 scoped_ptr
<syncer::SyncChangeProcessor
>(
888 new TestSyncProcessorStub(&out
)),
889 scoped_ptr
<syncer::SyncErrorFactory
>(
890 new syncer::SyncErrorFactoryMock()));
892 // There should be two changes, one for the fully associated tab, and
893 // one for the tab_id update to t1. t2 shouldn't need to be updated.
894 ASSERT_EQ(2U, FilterOutLocalHeaderChanges(&out
)->size());
895 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[0].change_type());
896 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[1].change_type());
898 out
[1].sync_data().GetSpecifics().session().tab().tab_id());
901 SessionsSyncManager::TabLinksMap tab_map
= manager()->local_tab_map_
;
902 ASSERT_EQ(3U, tab_map
.size());
903 int t2_tab_id
= t2
.GetSpecifics().session().tab().tab_id();
904 EXPECT_EQ(2, tab_map
.find(t2_tab_id
)->second
->tab_node_id());
905 EXPECT_EQ(1, tab_map
.find(kNewTabId
)->second
->tab_node_id());
906 int t0_tab_id
= out
[0].sync_data().GetSpecifics().session().tab().tab_id();
907 EXPECT_EQ(0, tab_map
.find(t0_tab_id
)->second
->tab_node_id());
908 // TODO(tim): Once bug 337057 is fixed, we can issue an OnLocalTabModified
909 // from here (using an override similar to above) to return a new tab id
910 // and verify that we don't see any node creations in the SyncChangeProcessor
911 // (similar to how SessionsSyncManagerTest.OnLocalTabModified works.)
914 // Tests MergeDataAndStartSyncing with sync data but no local data.
915 TEST_F(SessionsSyncManagerTest
, MergeWithInitialForeignSession
) {
916 std::string tag
= "tag1";
918 SessionID::id_type n1
[] = {5, 10, 13, 17};
919 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
920 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
921 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
922 tag
, tab_list1
, &tabs1
));
923 // Add a second window.
924 SessionID::id_type n2
[] = {7, 15, 18, 20};
925 std::vector
<SessionID::id_type
> tab_list2(n2
, n2
+ arraysize(n2
));
926 helper()->AddWindowSpecifics(1, tab_list2
, &meta
);
928 // Set up initial data.
929 syncer::SyncDataList initial_data
;
930 sync_pb::EntitySpecifics entity
;
931 entity
.mutable_session()->CopyFrom(meta
);
932 initial_data
.push_back(SyncData::CreateRemoteData(
936 syncer::AttachmentIdList(),
937 syncer::AttachmentServiceProxyForTest::Create()));
938 AddTabsToSyncDataList(tabs1
, &initial_data
);
940 for (size_t i
= 0; i
< tab_list2
.size(); ++i
) {
941 sync_pb::EntitySpecifics entity
;
942 helper()->BuildTabSpecifics(tag
, 0, tab_list2
[i
],
943 entity
.mutable_session());
944 initial_data
.push_back(SyncData::CreateRemoteData(
948 syncer::AttachmentIdList(),
949 syncer::AttachmentServiceProxyForTest::Create()));
952 syncer::SyncChangeList output
;
953 InitWithSyncDataTakeOutput(initial_data
, &output
);
954 EXPECT_TRUE(FilterOutLocalHeaderChanges(&output
)->empty());
956 std::vector
<const SyncedSession
*> foreign_sessions
;
957 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
958 ASSERT_EQ(1U, foreign_sessions
.size());
959 std::vector
<std::vector
<SessionID::id_type
> > session_reference
;
960 session_reference
.push_back(tab_list1
);
961 session_reference
.push_back(tab_list2
);
962 helper()->VerifySyncedSession(tag
, session_reference
, *(foreign_sessions
[0]));
965 // This is a combination of MergeWithInitialForeignSession and
966 // MergeLocalSessionExistingTabs. We repeat some checks performed in each of
967 // those tests to ensure the common mixed scenario works.
968 TEST_F(SessionsSyncManagerTest
, MergeWithLocalAndForeignTabs
) {
970 AddTab(browser(), GURL("http://foo1"));
971 NavigateAndCommitActiveTab(GURL("http://foo2"));
974 std::string tag
= "tag1";
975 SessionID::id_type n1
[] = {5, 10, 13, 17};
976 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
977 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
978 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
979 tag
, tab_list1
, &tabs1
));
980 syncer::SyncDataList foreign_data
;
981 sync_pb::EntitySpecifics entity
;
982 entity
.mutable_session()->CopyFrom(meta
);
983 foreign_data
.push_back(SyncData::CreateRemoteData(
987 syncer::AttachmentIdList(),
988 syncer::AttachmentServiceProxyForTest::Create()));
989 AddTabsToSyncDataList(tabs1
, &foreign_data
);
991 syncer::SyncChangeList output
;
992 InitWithSyncDataTakeOutput(foreign_data
, &output
);
993 ASSERT_EQ(4U, output
.size());
995 // Verify the local header.
996 EXPECT_TRUE(output
[0].IsValid());
997 EXPECT_EQ(SyncChange::ACTION_ADD
, output
[0].change_type());
998 const SyncData
data(output
[0].sync_data());
999 EXPECT_EQ(manager()->current_machine_tag(),
1000 syncer::SyncDataLocal(data
).GetTag());
1001 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1002 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1003 EXPECT_TRUE(specifics
.has_header());
1004 const sync_pb::SessionHeader
& header_s
= specifics
.header();
1005 EXPECT_TRUE(header_s
.has_device_type());
1006 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s
.client_name());
1007 EXPECT_EQ(0, header_s
.window_size());
1009 // Verify the tab node creations and updates with content.
1010 for (int i
= 1; i
< 3; i
++) {
1011 EXPECT_TRUE(output
[i
].IsValid());
1012 const SyncData
data(output
[i
].sync_data());
1013 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data
).GetTag(),
1014 manager()->current_machine_tag(), true));
1015 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1016 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1018 EXPECT_EQ(SyncChange::ACTION_ADD
, output
[1].change_type());
1019 EXPECT_EQ(SyncChange::ACTION_UPDATE
, output
[2].change_type());
1020 EXPECT_TRUE(output
[2].sync_data().GetSpecifics().session().has_tab());
1022 // Verify the header was updated to reflect window state.
1023 EXPECT_TRUE(output
[3].IsValid());
1024 EXPECT_EQ(SyncChange::ACTION_UPDATE
, output
[3].change_type());
1025 const SyncData
data_2(output
[3].sync_data());
1026 EXPECT_EQ(manager()->current_machine_tag(),
1027 syncer::SyncDataLocal(data_2
).GetTag());
1028 const sync_pb::SessionSpecifics
& specifics2(data_2
.GetSpecifics().session());
1029 EXPECT_EQ(manager()->current_machine_tag(), specifics2
.session_tag());
1030 EXPECT_TRUE(specifics2
.has_header());
1031 const sync_pb::SessionHeader
& header_s2
= specifics2
.header();
1032 EXPECT_EQ(1, header_s2
.window_size());
1034 // Verify foreign data.
1035 std::vector
<const SyncedSession
*> foreign_sessions
;
1036 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1037 std::vector
<std::vector
<SessionID::id_type
> > session_reference
;
1038 session_reference
.push_back(tab_list1
);
1039 helper()->VerifySyncedSession(tag
, session_reference
, *(foreign_sessions
[0]));
1040 // There should be one and only one foreign session. If VerifySyncedSession
1041 // was successful above this EXPECT call ensures the local session didn't
1042 // get mistakenly added to foreign tracking (Similar to ExistingTabs test).
1043 EXPECT_EQ(1U, foreign_sessions
.size());
1046 // Tests the common scenario. Merge with both local and foreign session data
1047 // followed by updates flowing from sync and local.
1048 TEST_F(SessionsSyncManagerTest
, UpdatesAfterMixedMerge
) {
1049 // Add local and foreign data.
1050 AddTab(browser(), GURL("http://foo1"));
1051 NavigateAndCommitActiveTab(GURL("http://foo2"));
1053 std::string tag1
= "tag1";
1054 syncer::SyncDataList foreign_data1
;
1055 std::vector
<std::vector
<SessionID::id_type
> > meta1_reference
;
1056 sync_pb::SessionSpecifics meta1
;
1058 SessionID::id_type n1
[] = {5, 10, 13, 17};
1059 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
1060 meta1_reference
.push_back(tab_list1
);
1061 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1062 meta1
= helper()->BuildForeignSession(tag1
, tab_list1
, &tabs1
);
1063 sync_pb::EntitySpecifics entity
;
1064 entity
.mutable_session()->CopyFrom(meta1
);
1065 foreign_data1
.push_back(SyncData::CreateRemoteData(
1069 syncer::AttachmentIdList(),
1070 syncer::AttachmentServiceProxyForTest::Create()));
1071 AddTabsToSyncDataList(tabs1
, &foreign_data1
);
1073 syncer::SyncChangeList output1
;
1074 InitWithSyncDataTakeOutput(foreign_data1
, &output1
);
1075 ASSERT_EQ(4U, output1
.size());
1077 // Add a second window to the foreign session.
1078 // TODO(tim): Bug 98892. Add local window too when observers are hooked up.
1079 SessionID::id_type tab_nums2
[] = {7, 15, 18, 20};
1080 std::vector
<SessionID::id_type
> tab_list2(
1081 tab_nums2
, tab_nums2
+ arraysize(tab_nums2
));
1082 meta1_reference
.push_back(tab_list2
);
1083 helper()->AddWindowSpecifics(1, tab_list2
, &meta1
);
1084 std::vector
<sync_pb::SessionSpecifics
> tabs2
;
1085 tabs2
.resize(tab_list2
.size());
1086 for (size_t i
= 0; i
< tab_list2
.size(); ++i
) {
1087 helper()->BuildTabSpecifics(tag1
, 0, tab_list2
[i
], &tabs2
[i
]);
1090 syncer::SyncChangeList changes
;
1091 changes
.push_back(MakeRemoteChange(1, meta1
, SyncChange::ACTION_UPDATE
));
1092 AddTabsToChangeList(tabs2
, SyncChange::ACTION_ADD
, &changes
);
1093 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1096 // Check that the foreign session was associated and retrieve the data.
1097 std::vector
<const SyncedSession
*> foreign_sessions
;
1098 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1099 ASSERT_EQ(1U, foreign_sessions
.size());
1100 ASSERT_EQ(4U, foreign_sessions
[0]->windows
.find(0)->second
->tabs
.size());
1101 ASSERT_EQ(4U, foreign_sessions
[0]->windows
.find(1)->second
->tabs
.size());
1102 helper()->VerifySyncedSession(tag1
, meta1_reference
, *(foreign_sessions
[0]));
1104 // Add a new foreign session.
1105 std::string tag2
= "tag2";
1106 SessionID::id_type n2
[] = {107, 115};
1107 std::vector
<SessionID::id_type
> tag2_tab_list(n2
, n2
+ arraysize(n2
));
1108 std::vector
<sync_pb::SessionSpecifics
> tag2_tabs
;
1109 sync_pb::SessionSpecifics
meta2(helper()->BuildForeignSession(
1110 tag2
, tag2_tab_list
, &tag2_tabs
));
1111 changes
.push_back(MakeRemoteChange(100, meta2
, SyncChange::ACTION_ADD
));
1112 AddTabsToChangeList(tag2_tabs
, SyncChange::ACTION_ADD
, &changes
);
1114 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1117 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1118 std::vector
<std::vector
<SessionID::id_type
> > meta2_reference
;
1119 meta2_reference
.push_back(tag2_tab_list
);
1120 ASSERT_EQ(2U, foreign_sessions
.size());
1121 ASSERT_EQ(2U, foreign_sessions
[1]->windows
.find(0)->second
->tabs
.size());
1122 helper()->VerifySyncedSession(tag2
, meta2_reference
, *(foreign_sessions
[1]));
1123 foreign_sessions
.clear();
1125 // Remove a tab from a window.
1126 meta1_reference
[0].pop_back();
1127 tab_list1
.pop_back();
1128 sync_pb::SessionWindow
* win
= meta1
.mutable_header()->mutable_window(0);
1130 for (std::vector
<int>::const_iterator iter
= tab_list1
.begin();
1131 iter
!= tab_list1
.end(); ++iter
) {
1132 win
->add_tab(*iter
);
1134 syncer::SyncChangeList removal
;
1135 removal
.push_back(MakeRemoteChange(1, meta1
, SyncChange::ACTION_UPDATE
));
1136 AddTabsToChangeList(tabs1
, SyncChange::ACTION_UPDATE
, &removal
);
1137 manager()->ProcessSyncChanges(FROM_HERE
, removal
);
1139 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1140 ASSERT_EQ(2U, foreign_sessions
.size());
1141 ASSERT_EQ(3U, foreign_sessions
[0]->windows
.find(0)->second
->tabs
.size());
1142 helper()->VerifySyncedSession(tag1
, meta1_reference
, *(foreign_sessions
[0]));
1145 // Tests that this SyncSessionManager knows how to delete foreign sessions
1147 TEST_F(SessionsSyncManagerTest
, DeleteForeignSession
) {
1148 InitWithNoSyncData();
1149 std::string tag
= "tag1";
1150 syncer::SyncChangeList changes
;
1152 std::vector
<const SyncedSession
*> foreign_sessions
;
1153 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions
));
1154 manager()->DeleteForeignSessionInternal(tag
, &changes
);
1155 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions
));
1156 EXPECT_TRUE(changes
.empty());
1158 // Fill an instance of session specifics with a foreign session's data.
1159 std::vector
<sync_pb::SessionSpecifics
> tabs
;
1160 SessionID::id_type n1
[] = {5, 10, 13, 17};
1161 std::vector
<SessionID::id_type
> tab_nums1(n1
, n1
+ arraysize(n1
));
1162 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1163 tag
, tab_nums1
, &tabs
));
1165 // Update associator with the session's meta node, window, and tabs.
1166 manager()->UpdateTrackerWithForeignSession(meta
, base::Time());
1167 for (std::vector
<sync_pb::SessionSpecifics
>::iterator iter
= tabs
.begin();
1168 iter
!= tabs
.end(); ++iter
) {
1169 manager()->UpdateTrackerWithForeignSession(*iter
, base::Time());
1171 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1172 ASSERT_EQ(1U, foreign_sessions
.size());
1174 // Now delete the foreign session.
1175 manager()->DeleteForeignSessionInternal(tag
, &changes
);
1176 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions
));
1178 EXPECT_EQ(5U, changes
.size());
1179 std::set
<std::string
> expected_tags(&tag
, &tag
+ 1);
1180 for (int i
= 0; i
< 5; i
++)
1181 expected_tags
.insert(TabNodePool::TabIdToTag(tag
, i
));
1183 for (int i
= 0; i
< 5; i
++) {
1184 SCOPED_TRACE(changes
[i
].ToString());
1185 EXPECT_TRUE(changes
[i
].IsValid());
1186 EXPECT_EQ(SyncChange::ACTION_DELETE
, changes
[i
].change_type());
1187 EXPECT_TRUE(changes
[i
].sync_data().IsValid());
1189 expected_tags
.erase(
1190 syncer::SyncDataLocal(changes
[i
].sync_data()).GetTag()));
1194 // Write a foreign session to a node, with the tabs arriving first, and then
1196 TEST_F(SessionsSyncManagerTest
, WriteForeignSessionToNodeTabsFirst
) {
1197 InitWithNoSyncData();
1199 // Fill an instance of session specifics with a foreign session's data.
1200 std::string tag
= "tag1";
1201 SessionID::id_type nums1
[] = {5, 10, 13, 17};
1202 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1203 std::vector
<SessionID::id_type
> tab_list1 (nums1
, nums1
+ arraysize(nums1
));
1204 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1205 tag
, tab_list1
, &tabs1
));
1207 syncer::SyncChangeList adds
;
1208 // Add tabs for first window, then the meta node.
1209 AddTabsToChangeList(tabs1
, SyncChange::ACTION_ADD
, &adds
);
1210 adds
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_ADD
));
1211 manager()->ProcessSyncChanges(FROM_HERE
, adds
);
1213 // Check that the foreign session was associated and retrieve the data.
1214 std::vector
<const SyncedSession
*> foreign_sessions
;
1215 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1216 ASSERT_EQ(1U, foreign_sessions
.size());
1217 std::vector
<std::vector
<SessionID::id_type
> > session_reference
;
1218 session_reference
.push_back(tab_list1
);
1219 helper()->VerifySyncedSession(tag
, session_reference
, *(foreign_sessions
[0]));
1222 // Write a foreign session to a node with some tabs that never arrive.
1223 TEST_F(SessionsSyncManagerTest
, WriteForeignSessionToNodeMissingTabs
) {
1224 InitWithNoSyncData();
1226 // Fill an instance of session specifics with a foreign session's data.
1227 std::string tag
= "tag1";
1228 SessionID::id_type nums1
[] = {5, 10, 13, 17};
1229 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1230 std::vector
<SessionID::id_type
> tab_list1 (nums1
, nums1
+ arraysize(nums1
));
1231 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1232 tag
, tab_list1
, &tabs1
));
1233 // Add a second window, but this time only create two tab nodes, despite the
1234 // window expecting four tabs.
1235 SessionID::id_type tab_nums2
[] = {7, 15, 18, 20};
1236 std::vector
<SessionID::id_type
> tab_list2(
1237 tab_nums2
, tab_nums2
+ arraysize(tab_nums2
));
1238 helper()->AddWindowSpecifics(1, tab_list2
, &meta
);
1239 std::vector
<sync_pb::SessionSpecifics
> tabs2
;
1241 for (size_t i
= 0; i
< 2; ++i
) {
1242 helper()->BuildTabSpecifics(tag
, 0, tab_list2
[i
], &tabs2
[i
]);
1245 syncer::SyncChangeList changes
;
1246 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_ADD
));
1247 AddTabsToChangeList(tabs1
, SyncChange::ACTION_ADD
, &changes
);
1248 AddTabsToChangeList(tabs2
, SyncChange::ACTION_ADD
, &changes
);
1249 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1252 // Check that the foreign session was associated and retrieve the data.
1253 std::vector
<const SyncedSession
*> foreign_sessions
;
1254 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1255 ASSERT_EQ(1U, foreign_sessions
.size());
1256 ASSERT_EQ(2U, foreign_sessions
[0]->windows
.size());
1257 ASSERT_EQ(4U, foreign_sessions
[0]->windows
.find(0)->second
->tabs
.size());
1258 ASSERT_EQ(4U, foreign_sessions
[0]->windows
.find(1)->second
->tabs
.size());
1260 // Close the second window.
1261 meta
.mutable_header()->clear_window();
1262 helper()->AddWindowSpecifics(0, tab_list1
, &meta
);
1263 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_UPDATE
));
1264 // Update associator with the session's meta node containing one window.
1265 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1267 // Check that the foreign session was associated and retrieve the data.
1268 foreign_sessions
.clear();
1269 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1270 ASSERT_EQ(1U, foreign_sessions
.size());
1271 ASSERT_EQ(1U, foreign_sessions
[0]->windows
.size());
1272 std::vector
<std::vector
<SessionID::id_type
> > session_reference
;
1273 session_reference
.push_back(tab_list1
);
1274 helper()->VerifySyncedSession(tag
, session_reference
, *(foreign_sessions
[0]));
1277 // Tests that the SessionsSyncManager can handle a remote client deleting
1278 // sync nodes that belong to this local session.
1279 TEST_F(SessionsSyncManagerTest
, ProcessRemoteDeleteOfLocalSession
) {
1280 syncer::SyncChangeList out
;
1281 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
1282 ASSERT_EQ(2U, out
.size());
1283 sync_pb::EntitySpecifics
entity(out
[0].sync_data().GetSpecifics());
1284 SyncData
d(SyncData::CreateRemoteData(
1288 syncer::AttachmentIdList(),
1289 syncer::AttachmentServiceProxyForTest::Create()));
1290 SetSyncData(syncer::SyncDataList(&d
, &d
+ 1));
1293 syncer::SyncChangeList changes
;
1295 MakeRemoteChange(1, entity
.session(), SyncChange::ACTION_DELETE
));
1296 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1297 EXPECT_TRUE(manager()->local_tab_pool_out_of_sync_
);
1298 EXPECT_TRUE(out
.empty()); // ChangeProcessor shouldn't see any activity.
1300 // This should trigger repair of the TabNodePool.
1301 const GURL
foo1("http://foo/1");
1302 AddTab(browser(), foo1
);
1303 EXPECT_FALSE(manager()->local_tab_pool_out_of_sync_
);
1305 // AddTab triggers two notifications, one for the tab insertion and one for
1306 // committing the NavigationEntry. The first notification results in a tab
1307 // we don't associate although we do update the header node. The second
1308 // notification triggers association of the tab, and the subsequent window
1309 // update. So we should see 4 changes at the SyncChangeProcessor.
1310 ASSERT_EQ(4U, out
.size());
1312 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[0].change_type());
1313 ASSERT_TRUE(out
[0].sync_data().GetSpecifics().session().has_header());
1314 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[1].change_type());
1315 int tab_node_id
= out
[1].sync_data().GetSpecifics().session().tab_node_id();
1316 EXPECT_EQ(TabNodePool::TabIdToTag(
1317 manager()->current_machine_tag(), tab_node_id
),
1318 syncer::SyncDataLocal(out
[1].sync_data()).GetTag());
1319 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[2].change_type());
1320 ASSERT_TRUE(out
[2].sync_data().GetSpecifics().session().has_tab());
1321 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[3].change_type());
1322 ASSERT_TRUE(out
[3].sync_data().GetSpecifics().session().has_header());
1324 // Verify the actual content.
1325 const sync_pb::SessionHeader
& session_header
=
1326 out
[3].sync_data().GetSpecifics().session().header();
1327 ASSERT_EQ(1, session_header
.window_size());
1328 EXPECT_EQ(1, session_header
.window(0).tab_size());
1329 const sync_pb::SessionTab
& tab1
=
1330 out
[2].sync_data().GetSpecifics().session().tab();
1331 ASSERT_EQ(1, tab1
.navigation_size());
1332 EXPECT_EQ(foo1
.spec(), tab1
.navigation(0).virtual_url());
1334 // Verify TabNodePool integrity.
1335 EXPECT_EQ(1U, manager()->local_tab_pool_
.Capacity());
1336 EXPECT_TRUE(manager()->local_tab_pool_
.Empty());
1339 SessionsSyncManager::TabLinksMap tab_map
= manager()->local_tab_map_
;
1340 ASSERT_EQ(1U, tab_map
.size());
1341 int tab_id
= out
[2].sync_data().GetSpecifics().session().tab().tab_id();
1342 EXPECT_EQ(tab_node_id
, tab_map
.find(tab_id
)->second
->tab_node_id());
1345 // Test that receiving a session delete from sync removes the session
1347 TEST_F(SessionsSyncManagerTest
, ProcessForeignDelete
) {
1348 InitWithNoSyncData();
1349 SessionID::id_type n
[] = {5};
1350 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1351 std::vector
<SessionID::id_type
> tab_list(n
, n
+ arraysize(n
));
1352 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1353 "tag1", tab_list
, &tabs1
));
1355 syncer::SyncChangeList changes
;
1356 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_ADD
));
1357 AddTabsToChangeList(tabs1
, SyncChange::ACTION_ADD
, &changes
);
1358 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1360 std::vector
<const SyncedSession
*> foreign_sessions
;
1361 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1362 ASSERT_EQ(1U, foreign_sessions
.size());
1365 foreign_sessions
.clear();
1366 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_DELETE
));
1367 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1369 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions
));
1372 // TODO(shashishekhar): "Move this to TabNodePool unittests."
1373 TEST_F(SessionsSyncManagerTest
, SaveUnassociatedNodesForReassociation
) {
1374 syncer::SyncChangeList changes
;
1375 InitWithNoSyncData();
1377 std::string local_tag
= manager()->current_machine_tag();
1378 // Create a free node and then dissassociate sessions so that it ends up
1380 manager()->local_tab_pool_
.GetFreeTabNode(&changes
);
1382 // Update the tab_id of the node, so that it is considered a valid
1383 // unassociated node otherwise it will be mistaken for a corrupted node and
1384 // will be deleted before being added to the tab node pool.
1385 sync_pb::EntitySpecifics
entity(changes
[0].sync_data().GetSpecifics());
1386 entity
.mutable_session()->mutable_tab()->set_tab_id(1);
1387 SyncData
d(SyncData::CreateRemoteData(
1391 syncer::AttachmentIdList(),
1392 syncer::AttachmentServiceProxyForTest::Create()));
1393 syncer::SyncDataList
in(&d
, &d
+ 1);
1395 SessionsSyncManager
manager2(profile(), local_device(), NewDummyRouter());
1396 syncer::SyncMergeResult result
= manager2
.MergeDataAndStartSyncing(
1397 syncer::SESSIONS
, in
,
1398 scoped_ptr
<syncer::SyncChangeProcessor
>(
1399 new TestSyncProcessorStub(&changes
)),
1400 scoped_ptr
<syncer::SyncErrorFactory
>(
1401 new syncer::SyncErrorFactoryMock()));
1402 ASSERT_FALSE(result
.error().IsSet());
1403 EXPECT_TRUE(FilterOutLocalHeaderChanges(&changes
)->empty());
1406 TEST_F(SessionsSyncManagerTest
, MergeDeletesCorruptNode
) {
1407 syncer::SyncChangeList changes
;
1408 InitWithNoSyncData();
1410 std::string local_tag
= manager()->current_machine_tag();
1411 int tab_node_id
= manager()->local_tab_pool_
.GetFreeTabNode(&changes
);
1412 SyncData
d(SyncData::CreateRemoteData(
1414 changes
[0].sync_data().GetSpecifics(),
1416 syncer::AttachmentIdList(),
1417 syncer::AttachmentServiceProxyForTest::Create()));
1418 syncer::SyncDataList
in(&d
, &d
+ 1);
1422 InitWithSyncDataTakeOutput(in
, &changes
);
1423 EXPECT_EQ(1U, FilterOutLocalHeaderChanges(&changes
)->size());
1424 EXPECT_EQ(SyncChange::ACTION_DELETE
, changes
[0].change_type());
1425 EXPECT_EQ(TabNodePool::TabIdToTag(local_tag
, tab_node_id
),
1426 syncer::SyncDataLocal(changes
[0].sync_data()).GetTag());
1429 // Test that things work if a tab is initially ignored.
1430 TEST_F(SessionsSyncManagerTest
, AssociateWindowsDontReloadTabs
) {
1431 syncer::SyncChangeList out
;
1432 // Go to a URL that is ignored by session syncing.
1433 AddTab(browser(), GURL("chrome://preferences/"));
1434 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
1435 ASSERT_EQ(2U, out
.size()); // Header add and update.
1438 out
[1].sync_data().GetSpecifics().session().header().window_size());
1441 // Go to a sync-interesting URL.
1442 NavigateAndCommitActiveTab(GURL("http://foo2"));
1444 EXPECT_EQ(3U, out
.size()); // Tab add, update, and header update.
1447 StartsWithASCII(syncer::SyncDataLocal(out
[0].sync_data()).GetTag(),
1448 manager()->current_machine_tag(),
1450 EXPECT_EQ(manager()->current_machine_tag(),
1451 out
[0].sync_data().GetSpecifics().session().session_tag());
1452 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[0].change_type());
1455 StartsWithASCII(syncer::SyncDataLocal(out
[1].sync_data()).GetTag(),
1456 manager()->current_machine_tag(),
1458 EXPECT_EQ(manager()->current_machine_tag(),
1459 out
[1].sync_data().GetSpecifics().session().session_tag());
1460 EXPECT_TRUE(out
[1].sync_data().GetSpecifics().session().has_tab());
1461 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[1].change_type());
1463 EXPECT_TRUE(out
[2].IsValid());
1464 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[2].change_type());
1465 const SyncData
data(out
[2].sync_data());
1466 EXPECT_EQ(manager()->current_machine_tag(),
1467 syncer::SyncDataLocal(data
).GetTag());
1468 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1469 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1470 EXPECT_TRUE(specifics
.has_header());
1471 const sync_pb::SessionHeader
& header_s
= specifics
.header();
1472 EXPECT_EQ(1, header_s
.window_size());
1473 EXPECT_EQ(1, header_s
.window(0).tab_size());
1476 // Tests that the SyncSessionManager responds to local tab events properly.
1477 TEST_F(SessionsSyncManagerTest
, OnLocalTabModified
) {
1478 syncer::SyncChangeList out
;
1479 // Init with no local data, relies on MergeLocalSessionNoTabs.
1480 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
1481 ASSERT_FALSE(manager()->current_machine_tag().empty());
1482 ASSERT_EQ(2U, out
.size());
1484 // Copy the original header.
1485 sync_pb::EntitySpecifics
header(out
[0].sync_data().GetSpecifics());
1488 const GURL
foo1("http://foo/1");
1489 const GURL
foo2("http://foo/2");
1490 const GURL
bar1("http://bar/1");
1491 const GURL
bar2("http://bar/2");
1492 AddTab(browser(), foo1
);
1493 NavigateAndCommitActiveTab(foo2
);
1494 AddTab(browser(), bar1
);
1495 NavigateAndCommitActiveTab(bar2
);
1497 // One add, one update for each AddTab.
1498 // One update for each NavigateAndCommit.
1499 // = 6 total tab updates.
1500 // One header update corresponding to each of those.
1501 // = 6 total header updates.
1502 // 12 total updates.
1503 ASSERT_EQ(12U, out
.size());
1505 // Verify the tab node creations and updates to ensure the SyncProcessor
1506 // sees the right operations.
1507 for (int i
= 0; i
< 12; i
++) {
1509 EXPECT_TRUE(out
[i
].IsValid());
1510 const SyncData
data(out
[i
].sync_data());
1511 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data
).GetTag(),
1512 manager()->current_machine_tag(), true));
1513 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1514 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1516 // First thing on an AddTab is a no-op header update for parented tab.
1517 EXPECT_EQ(header
.SerializeAsString(),
1518 data
.GetSpecifics().SerializeAsString());
1519 EXPECT_EQ(manager()->current_machine_tag(),
1520 syncer::SyncDataLocal(data
).GetTag());
1521 } else if (i
% 6 == 1) {
1522 // Next, the TabNodePool should create the tab node.
1523 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[i
].change_type());
1524 EXPECT_EQ(TabNodePool::TabIdToTag(
1525 manager()->current_machine_tag(),
1526 data
.GetSpecifics().session().tab_node_id()),
1527 syncer::SyncDataLocal(data
).GetTag());
1528 } else if (i
% 6 == 2) {
1529 // Then we see the tab update to the URL.
1530 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[i
].change_type());
1531 EXPECT_EQ(TabNodePool::TabIdToTag(
1532 manager()->current_machine_tag(),
1533 data
.GetSpecifics().session().tab_node_id()),
1534 syncer::SyncDataLocal(data
).GetTag());
1535 ASSERT_TRUE(specifics
.has_tab());
1536 } else if (i
% 6 == 3) {
1537 // The header needs to be updated to reflect the new window state.
1538 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[i
].change_type());
1539 EXPECT_TRUE(specifics
.has_header());
1540 } else if (i
% 6 == 4) {
1541 // Now we move on to NavigateAndCommit. Update the tab.
1542 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[i
].change_type());
1543 EXPECT_EQ(TabNodePool::TabIdToTag(
1544 manager()->current_machine_tag(),
1545 data
.GetSpecifics().session().tab_node_id()),
1546 syncer::SyncDataLocal(data
).GetTag());
1547 ASSERT_TRUE(specifics
.has_tab());
1548 } else if (i
% 6 == 5) {
1549 // The header needs to be updated to reflect the new window state.
1550 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[i
].change_type());
1551 ASSERT_TRUE(specifics
.has_header());
1552 header
= data
.GetSpecifics();
1556 // Verify the actual content to ensure sync sees the right data.
1557 // When it's all said and done, the header should reflect two tabs.
1558 const sync_pb::SessionHeader
& session_header
= header
.session().header();
1559 ASSERT_EQ(1, session_header
.window_size());
1560 EXPECT_EQ(2, session_header
.window(0).tab_size());
1562 // ASSERT_TRUEs above allow us to dive in freely here.
1563 // Verify first tab.
1564 const sync_pb::SessionTab
& tab1_1
=
1565 out
[2].sync_data().GetSpecifics().session().tab();
1566 ASSERT_EQ(1, tab1_1
.navigation_size());
1567 EXPECT_EQ(foo1
.spec(), tab1_1
.navigation(0).virtual_url());
1568 const sync_pb::SessionTab
& tab1_2
=
1569 out
[4].sync_data().GetSpecifics().session().tab();
1570 ASSERT_EQ(2, tab1_2
.navigation_size());
1571 EXPECT_EQ(foo1
.spec(), tab1_2
.navigation(0).virtual_url());
1572 EXPECT_EQ(foo2
.spec(), tab1_2
.navigation(1).virtual_url());
1574 // Verify second tab.
1575 const sync_pb::SessionTab
& tab2_1
=
1576 out
[8].sync_data().GetSpecifics().session().tab();
1577 ASSERT_EQ(1, tab2_1
.navigation_size());
1578 EXPECT_EQ(bar1
.spec(), tab2_1
.navigation(0).virtual_url());
1579 const sync_pb::SessionTab
& tab2_2
=
1580 out
[10].sync_data().GetSpecifics().session().tab();
1581 ASSERT_EQ(2, tab2_2
.navigation_size());
1582 EXPECT_EQ(bar1
.spec(), tab2_2
.navigation(0).virtual_url());
1583 EXPECT_EQ(bar2
.spec(), tab2_2
.navigation(1).virtual_url());
1586 // Ensure model association associates the pre-existing tabs.
1587 TEST_F(SessionsSyncManagerTest
, MergeLocalSessionExistingTabs
) {
1588 AddTab(browser(), GURL("http://foo1"));
1589 NavigateAndCommitActiveTab(GURL("http://foo2")); // Adds back entry.
1590 AddTab(browser(), GURL("http://bar1"));
1591 NavigateAndCommitActiveTab(GURL("http://bar2")); // Adds back entry.
1593 syncer::SyncChangeList out
;
1594 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
1595 ASSERT_EQ(6U, out
.size());
1597 // Check that this machine's data is not included in the foreign windows.
1598 std::vector
<const SyncedSession
*> foreign_sessions
;
1599 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions
));
1601 // Verify the header.
1602 EXPECT_TRUE(out
[0].IsValid());
1603 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[0].change_type());
1604 const SyncData
data(out
[0].sync_data());
1605 EXPECT_EQ(manager()->current_machine_tag(),
1606 syncer::SyncDataLocal(data
).GetTag());
1607 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1608 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1609 EXPECT_TRUE(specifics
.has_header());
1610 const sync_pb::SessionHeader
& header_s
= specifics
.header();
1611 EXPECT_TRUE(header_s
.has_device_type());
1612 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s
.client_name());
1613 EXPECT_EQ(0, header_s
.window_size());
1615 // Verify the tab node creations and updates with content.
1616 for (int i
= 1; i
< 5; i
++) {
1617 EXPECT_TRUE(out
[i
].IsValid());
1618 const SyncData
data(out
[i
].sync_data());
1619 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data
).GetTag(),
1620 manager()->current_machine_tag(), true));
1621 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1622 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1624 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[i
].change_type());
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());
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());
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(
1689 syncer::AttachmentIdList(),
1690 syncer::AttachmentServiceProxyForTest::Create()));
1691 foreign_data
.push_back(SyncData::CreateRemoteData(
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());
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(
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
;
1761 syncer::SyncChange(FROM_HERE
,
1762 SyncChange::ACTION_UPDATE
,
1763 syncer::SyncData::CreateRemoteData(
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());
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());
1837 class SessionNotificationObserver
: public content::NotificationObserver
{
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
{
1850 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED
:
1851 notified_of_update_
= true;
1853 case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL
:
1854 notified_of_refresh_
= true;
1861 bool notified_of_update() const { return notified_of_update_
; }
1862 bool notified_of_refresh() const { return notified_of_refresh_
; }
1864 notified_of_update_
= false;
1865 notified_of_refresh_
= false;
1868 content::NotificationRegistrar registrar_
;
1869 bool notified_of_update_
;
1870 bool notified_of_refresh_
;
1874 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when processing
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());
1894 AddTabsToChangeList(tabs1
, SyncChange::ACTION_ADD
, &changes
);
1895 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1896 EXPECT_TRUE(observer
.notified_of_update());
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(
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
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(
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(
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(
2034 syncer::AttachmentIdList(),
2035 syncer::AttachmentServiceProxyForTest::Create()));
2039 for (size_t i
= 0; i
< tabs1
.size(); i
++) {
2040 entity
.mutable_session()->CopyFrom(tabs1
[i
]);
2041 initial_data
.push_back(SyncData::CreateRemoteData(
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(
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(
2071 base::Time::FromDoubleT(3000),
2072 syncer::AttachmentIdList(),
2073 syncer::AttachmentServiceProxyForTest::Create()));
2075 syncer::SyncChangeList output
;
2076 InitWithSyncDataTakeOutput(initial_data
, &output
);
2078 std::vector
<const SyncedSession
*> foreign_sessions
;
2079 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
2081 const std::vector
<sessions::SessionTab
*>& window_tabs
=
2082 foreign_sessions
[0]->windows
.find(0)->second
->tabs
;
2083 ASSERT_EQ(3U, window_tabs
.size());
2084 // The first one is from the original set of tabs.
2085 ASSERT_EQ(1, window_tabs
[0]->tab_visual_index
);
2086 // The one from the original set of tabs wins over duplicating_tab1.
2087 ASSERT_EQ(1, window_tabs
[1]->tab_visual_index
);
2088 // duplicating_tab2 wins due to the later timestamp.
2089 ASSERT_EQ(3, window_tabs
[2]->tab_visual_index
);
2092 } // namespace browser_sync