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