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 sync_driver::SyncedSession
;
42 using syncer::SyncChange
;
43 using syncer::SyncData
;
45 namespace browser_sync
{
49 class SyncedWindowDelegateOverride
: public SyncedWindowDelegate
{
51 explicit SyncedWindowDelegateOverride(const SyncedWindowDelegate
* wrapped
)
54 ~SyncedWindowDelegateOverride() override
{}
56 bool HasWindow() const override
{ return wrapped_
->HasWindow(); }
58 SessionID::id_type
GetSessionId() const override
{
59 return wrapped_
->GetSessionId();
62 int GetTabCount() const override
{ return wrapped_
->GetTabCount(); }
64 int GetActiveIndex() const override
{ return wrapped_
->GetActiveIndex(); }
66 bool IsApp() const override
{ return wrapped_
->IsApp(); }
68 bool IsTypeTabbed() const override
{ return wrapped_
->IsTypeTabbed(); }
70 bool IsTypePopup() const override
{ return wrapped_
->IsTypePopup(); }
72 bool IsTabPinned(const SyncedTabDelegate
* tab
) const override
{
73 return wrapped_
->IsTabPinned(tab
);
76 SyncedTabDelegate
* GetTabAt(int index
) const override
{
77 if (tab_overrides_
.find(index
) != tab_overrides_
.end())
78 return tab_overrides_
.find(index
)->second
;
80 return wrapped_
->GetTabAt(index
);
83 void OverrideTabAt(int index
,
84 SyncedTabDelegate
* delegate
,
85 SessionID::id_type tab_id
) {
86 tab_overrides_
[index
] = delegate
;
87 tab_id_overrides_
[index
] = tab_id
;
90 SessionID::id_type
GetTabIdAt(int index
) const override
{
91 if (tab_id_overrides_
.find(index
) != tab_id_overrides_
.end())
92 return tab_id_overrides_
.find(index
)->second
;
93 return wrapped_
->GetTabIdAt(index
);
96 bool IsSessionRestoreInProgress() const override
{
97 return wrapped_
->IsSessionRestoreInProgress();
100 bool ShouldSync() const override
{ return wrapped_
->ShouldSync(); }
103 std::map
<int, SyncedTabDelegate
*> tab_overrides_
;
104 std::map
<int, SessionID::id_type
> tab_id_overrides_
;
105 const SyncedWindowDelegate
* const wrapped_
;
108 class TestSyncedWindowDelegatesGetter
: public SyncedWindowDelegatesGetter
{
110 TestSyncedWindowDelegatesGetter(
111 const std::set
<const SyncedWindowDelegate
*>& delegates
)
112 : delegates_(delegates
) {}
114 std::set
<const SyncedWindowDelegate
*> GetSyncedWindowDelegates() override
{
118 const std::set
<const SyncedWindowDelegate
*> delegates_
;
121 class TestSyncProcessorStub
: public syncer::SyncChangeProcessor
{
123 explicit TestSyncProcessorStub(syncer::SyncChangeList
* output
)
125 syncer::SyncError
ProcessSyncChanges(
126 const tracked_objects::Location
& from_here
,
127 const syncer::SyncChangeList
& change_list
) override
{
128 if (error_
.IsSet()) {
129 syncer::SyncError error
= error_
;
130 error_
= syncer::SyncError();
135 output_
->insert(output_
->end(), change_list
.begin(), change_list
.end());
137 return syncer::SyncError();
140 syncer::SyncDataList
GetAllSyncData(syncer::ModelType type
) const override
{
141 return sync_data_to_return_
;
144 void FailProcessSyncChangesWith(const syncer::SyncError
& error
) {
148 void SetSyncDataToReturn(const syncer::SyncDataList
& data
) {
149 sync_data_to_return_
= data
;
153 syncer::SyncError error_
;
154 syncer::SyncChangeList
* output_
;
155 syncer::SyncDataList sync_data_to_return_
;
158 syncer::SyncChange
MakeRemoteChange(
160 const sync_pb::SessionSpecifics
& specifics
,
161 SyncChange::SyncChangeType type
) {
162 sync_pb::EntitySpecifics entity
;
163 entity
.mutable_session()->CopyFrom(specifics
);
164 return syncer::SyncChange(
167 syncer::SyncData::CreateRemoteData(
171 syncer::AttachmentIdList(),
172 syncer::AttachmentServiceProxyForTest::Create()));
175 void AddTabsToChangeList(
176 const std::vector
<sync_pb::SessionSpecifics
>& batch
,
177 SyncChange::SyncChangeType type
,
178 syncer::SyncChangeList
* change_list
) {
179 std::vector
<sync_pb::SessionSpecifics
>::const_iterator iter
;
180 for (iter
= batch
.begin();
181 iter
!= batch
.end(); ++iter
) {
182 sync_pb::EntitySpecifics entity
;
183 entity
.mutable_session()->CopyFrom(*iter
);
184 change_list
->push_back(syncer::SyncChange(
187 syncer::SyncData::CreateRemoteData(
191 syncer::AttachmentIdList(),
192 syncer::AttachmentServiceProxyForTest::Create())));
196 void AddTabsToSyncDataList(const std::vector
<sync_pb::SessionSpecifics
> tabs
,
197 syncer::SyncDataList
* list
) {
198 for (size_t i
= 0; i
< tabs
.size(); i
++) {
199 sync_pb::EntitySpecifics entity
;
200 entity
.mutable_session()->CopyFrom(tabs
[i
]);
201 list
->push_back(SyncData::CreateRemoteData(
204 base::Time::FromInternalValue(i
+ 1),
205 syncer::AttachmentIdList(),
206 syncer::AttachmentServiceProxyForTest::Create()));
210 // Creates a field trial with the specified |trial_name| and |group_name| and
211 // registers an associated |variation_id| for it for the given |service|.
212 void CreateAndActivateFieldTrial(const std::string
& trial_name
,
213 const std::string
& group_name
,
214 variations::VariationID variation_id
,
215 variations::IDCollectionKey service
) {
216 base::FieldTrialList::CreateFieldTrial(trial_name
, group_name
);
217 variations::AssociateGoogleVariationID(service
, trial_name
, group_name
,
219 // Access the trial to activate it.
220 base::FieldTrialList::FindFullName(trial_name
);
223 class DummyRouter
: public LocalSessionEventRouter
{
225 ~DummyRouter() override
{}
226 void StartRoutingTo(LocalSessionEventHandler
* handler
) override
{}
227 void Stop() override
{}
230 scoped_ptr
<LocalSessionEventRouter
> NewDummyRouter() {
231 return scoped_ptr
<LocalSessionEventRouter
>(new DummyRouter());
236 class SessionsSyncManagerTest
237 : public BrowserWithTestWindowTest
{
239 SessionsSyncManagerTest()
240 : test_processor_(NULL
) {
241 local_device_
.reset(new LocalDeviceInfoProviderMock(
243 "Wayne Gretzky's Hacking Box",
246 sync_pb::SyncEnums_DeviceType_TYPE_LINUX
,
250 void SetUp() override
{
251 BrowserWithTestWindowTest::SetUp();
252 browser_sync::NotificationServiceSessionsRouter
* router(
253 new browser_sync::NotificationServiceSessionsRouter(
254 profile(), syncer::SyncableService::StartSyncFlare()));
255 manager_
.reset(new SessionsSyncManager(profile(), local_device_
.get(),
256 scoped_ptr
<LocalSessionEventRouter
>(router
)));
259 void TearDown() override
{
260 test_processor_
= NULL
;
263 BrowserWithTestWindowTest::TearDown();
266 const DeviceInfo
* GetLocalDeviceInfo() {
267 return local_device_
->GetLocalDeviceInfo();
270 SessionsSyncManager
* manager() { return manager_
.get(); }
271 SessionSyncTestHelper
* helper() { return &helper_
; }
272 LocalDeviceInfoProvider
* local_device() { return local_device_
.get(); }
274 void InitWithSyncDataTakeOutput(const syncer::SyncDataList
& initial_data
,
275 syncer::SyncChangeList
* output
) {
276 test_processor_
= new TestSyncProcessorStub(output
);
277 syncer::SyncMergeResult result
= manager_
->MergeDataAndStartSyncing(
278 syncer::SESSIONS
, initial_data
,
279 scoped_ptr
<syncer::SyncChangeProcessor
>(test_processor_
),
280 scoped_ptr
<syncer::SyncErrorFactory
>(
281 new syncer::SyncErrorFactoryMock()));
282 EXPECT_FALSE(result
.error().IsSet());
285 void InitWithNoSyncData() {
286 InitWithSyncDataTakeOutput(syncer::SyncDataList(), NULL
);
289 void TriggerProcessSyncChangesError() {
290 test_processor_
->FailProcessSyncChangesWith(syncer::SyncError(
291 FROM_HERE
, syncer::SyncError::DATATYPE_ERROR
, "Error",
295 void SetSyncData(const syncer::SyncDataList
& data
) {
296 test_processor_
->SetSyncDataToReturn(data
);
299 syncer::SyncChangeList
* FilterOutLocalHeaderChanges(
300 syncer::SyncChangeList
* list
) {
301 syncer::SyncChangeList::iterator it
= list
->begin();
303 while (it
!= list
->end()) {
304 if (syncer::SyncDataLocal(it
->sync_data()).GetTag() ==
305 manager_
->current_machine_tag()) {
306 EXPECT_TRUE(SyncChange::ACTION_ADD
== it
->change_type() ||
307 SyncChange::ACTION_UPDATE
== it
->change_type());
308 it
= list
->erase(it
);
319 scoped_ptr
<SessionsSyncManager
> manager_
;
320 SessionSyncTestHelper helper_
;
321 TestSyncProcessorStub
* test_processor_
;
322 scoped_ptr
<LocalDeviceInfoProviderMock
> local_device_
;
325 // Test that the SyncSessionManager can properly fill in a SessionHeader.
326 TEST_F(SessionsSyncManagerTest
, PopulateSessionHeader
) {
327 sync_pb::SessionHeader header_s
;
328 header_s
.set_client_name("Client 1");
329 header_s
.set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_WIN
);
331 SyncedSession session
;
332 base::Time time
= base::Time::Now();
333 SessionsSyncManager::PopulateSessionHeaderFromSpecifics(
334 header_s
, time
, &session
);
335 ASSERT_EQ("Client 1", session
.session_name
);
336 ASSERT_EQ(SyncedSession::TYPE_WIN
, session
.device_type
);
337 ASSERT_EQ(time
, session
.modified_time
);
340 // Test translation between protobuf types and chrome session types.
341 TEST_F(SessionsSyncManagerTest
, PopulateSessionWindow
) {
342 sync_pb::SessionWindow window_s
;
344 window_s
.set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED
);
345 window_s
.set_selected_tab_index(1);
347 std::string tag
= "tag";
348 SyncedSession
* session
= manager()->session_tracker_
.GetSession(tag
);
349 manager()->session_tracker_
.PutWindowInSession(tag
, 0);
350 manager()->BuildSyncedSessionFromSpecifics(
351 tag
, window_s
, base::Time(), session
->windows
[0]);
352 ASSERT_EQ(1U, session
->windows
[0]->tabs
.size());
353 ASSERT_EQ(1, session
->windows
[0]->selected_tab_index
);
354 ASSERT_EQ(sessions::SessionWindow::TYPE_TABBED
, session
->windows
[0]->type
);
355 ASSERT_EQ(1U, manager()->session_tracker_
.num_synced_sessions());
357 manager()->session_tracker_
.num_synced_tabs(std::string("tag")));
362 class SyncedTabDelegateFake
: public SyncedTabDelegate
{
364 SyncedTabDelegateFake() : current_entry_index_(0),
365 pending_entry_index_(-1),
366 is_supervised_(false),
368 blocked_navigations_(NULL
) {}
369 ~SyncedTabDelegateFake() override
{}
371 int GetCurrentEntryIndex() const override
{ return current_entry_index_
; }
372 void set_current_entry_index(int i
) {
373 current_entry_index_
= i
;
376 content::NavigationEntry
* GetEntryAtIndex(int i
) const override
{
377 const int size
= entries_
.size();
378 return (size
< i
+ 1) ? NULL
: entries_
[i
];
381 void AppendEntry(scoped_ptr
<content::NavigationEntry
> entry
) {
382 entries_
.push_back(entry
.Pass());
385 int GetEntryCount() const override
{ return entries_
.size(); }
387 int GetPendingEntryIndex() const override
{ return pending_entry_index_
; }
388 void set_pending_entry_index(int i
) {
389 pending_entry_index_
= i
;
392 SessionID::id_type
GetWindowId() const override
{
393 return SessionID::id_type();
396 SessionID::id_type
GetSessionId() const override
{
397 return SessionID::id_type();
400 bool IsBeingDestroyed() const override
{ return false; }
401 Profile
* profile() const override
{ return NULL
; }
402 std::string
GetExtensionAppId() const override
{ return std::string(); }
403 content::NavigationEntry
* GetPendingEntry() const override
{ return NULL
; }
404 content::NavigationEntry
* GetActiveEntry() const override
{ return NULL
; }
405 bool ProfileIsSupervised() const override
{ return is_supervised_
; }
406 void set_is_supervised(bool is_supervised
) { is_supervised_
= is_supervised
; }
407 const std::vector
<const content::NavigationEntry
*>* GetBlockedNavigations()
409 return blocked_navigations_
;
411 void set_blocked_navigations(
412 std::vector
<const content::NavigationEntry
*>* navs
) {
413 blocked_navigations_
= navs
;
415 bool IsPinned() const override
{ return false; }
416 bool HasWebContents() const override
{ return false; }
417 content::WebContents
* GetWebContents() const override
{ return NULL
; }
419 // Session sync related methods.
420 int GetSyncId() const override
{ return sync_id_
; }
421 void SetSyncId(int sync_id
) override
{ sync_id_
= sync_id
; }
423 bool ShouldSync() const override
{
424 return sessions_util::ShouldSyncTab(*this);
428 current_entry_index_
= 0;
429 pending_entry_index_
= -1;
435 int current_entry_index_
;
436 int pending_entry_index_
;
439 std::vector
<const content::NavigationEntry
*>* blocked_navigations_
;
440 ScopedVector
<content::NavigationEntry
> entries_
;
445 // Test that we exclude tabs with only chrome:// and file:// schemed navigations
446 // from ShouldSyncTab(..).
447 TEST_F(SessionsSyncManagerTest
, ValidTabs
) {
448 SyncedTabDelegateFake tab
;
450 // A null entry shouldn't crash.
451 tab
.AppendEntry(NULL
);
452 EXPECT_FALSE(tab
.ShouldSync());
455 // A chrome:// entry isn't valid.
456 scoped_ptr
<content::NavigationEntry
> entry(
457 content::NavigationEntry::Create());
458 entry
->SetVirtualURL(GURL("chrome://preferences/"));
459 tab
.AppendEntry(entry
.Pass());
460 EXPECT_FALSE(tab
.ShouldSync());
463 // A file:// entry isn't valid, even in addition to another entry.
464 scoped_ptr
<content::NavigationEntry
> entry2(
465 content::NavigationEntry::Create());
466 entry2
->SetVirtualURL(GURL("file://bla"));
467 tab
.AppendEntry(entry2
.Pass());
468 EXPECT_FALSE(tab
.ShouldSync());
470 // Add a valid scheme entry to tab, making the tab valid.
471 scoped_ptr
<content::NavigationEntry
> entry3(
472 content::NavigationEntry::Create());
473 entry3
->SetVirtualURL(GURL("http://www.google.com"));
474 tab
.AppendEntry(entry3
.Pass());
475 EXPECT_FALSE(tab
.ShouldSync());
478 // Make sure GetCurrentVirtualURL() returns the virtual URL of the pending
479 // entry if the current entry is pending.
480 TEST_F(SessionsSyncManagerTest
, GetCurrentVirtualURLPending
) {
481 SyncedTabDelegateFake tab
;
482 scoped_ptr
<content::NavigationEntry
> entry(
483 content::NavigationEntry::Create());
484 GURL
url("http://www.google.com/");
485 entry
->SetVirtualURL(url
);
486 tab
.AppendEntry(entry
.Pass());
487 EXPECT_EQ(url
, manager()->GetCurrentVirtualURL(tab
));
490 // Make sure GetCurrentVirtualURL() returns the virtual URL of the current
491 // entry if the current entry is non-pending.
492 TEST_F(SessionsSyncManagerTest
, GetCurrentVirtualURLNonPending
) {
493 SyncedTabDelegateFake tab
;
494 scoped_ptr
<content::NavigationEntry
> entry(
495 content::NavigationEntry::Create());
496 GURL
url("http://www.google.com/");
497 entry
->SetVirtualURL(url
);
498 tab
.AppendEntry(entry
.Pass());
499 EXPECT_EQ(url
, manager()->GetCurrentVirtualURL(tab
));
502 static const base::Time kTime0
= base::Time::FromInternalValue(100);
503 static const base::Time kTime1
= base::Time::FromInternalValue(110);
504 static const base::Time kTime2
= base::Time::FromInternalValue(120);
505 static const base::Time kTime3
= base::Time::FromInternalValue(130);
506 static const base::Time kTime4
= base::Time::FromInternalValue(140);
507 static const base::Time kTime5
= base::Time::FromInternalValue(150);
508 static const base::Time kTime6
= base::Time::FromInternalValue(160);
509 static const base::Time kTime7
= base::Time::FromInternalValue(170);
510 static const base::Time kTime8
= base::Time::FromInternalValue(180);
511 static const base::Time kTime9
= base::Time::FromInternalValue(190);
513 // Populate the mock tab delegate with some data and navigation
514 // entries and make sure that setting a SessionTab from it preserves
515 // those entries (and clobbers any existing data).
516 TEST_F(SessionsSyncManagerTest
, SetSessionTabFromDelegate
) {
517 // Create a tab with three valid entries.
518 SyncedTabDelegateFake tab
;
519 scoped_ptr
<content::NavigationEntry
> entry1(
520 content::NavigationEntry::Create());
521 GURL
url1("http://www.google.com/");
522 entry1
->SetVirtualURL(url1
);
523 entry1
->SetTimestamp(kTime1
);
524 entry1
->SetHttpStatusCode(200);
525 scoped_ptr
<content::NavigationEntry
> entry2(
526 content::NavigationEntry::Create());
527 GURL
url2("http://www.noodle.com/");
528 entry2
->SetVirtualURL(url2
);
529 entry2
->SetTimestamp(kTime2
);
530 entry2
->SetHttpStatusCode(201);
531 scoped_ptr
<content::NavigationEntry
> entry3(
532 content::NavigationEntry::Create());
533 GURL
url3("http://www.doodle.com/");
534 entry3
->SetVirtualURL(url3
);
535 entry3
->SetTimestamp(kTime3
);
536 entry3
->SetHttpStatusCode(202);
538 tab
.AppendEntry(entry1
.Pass());
539 tab
.AppendEntry(entry2
.Pass());
540 tab
.AppendEntry(entry3
.Pass());
541 tab
.set_current_entry_index(2);
543 sessions::SessionTab session_tab
;
544 session_tab
.window_id
.set_id(1);
545 session_tab
.tab_id
.set_id(1);
546 session_tab
.tab_visual_index
= 1;
547 session_tab
.current_navigation_index
= 1;
548 session_tab
.pinned
= true;
549 session_tab
.extension_app_id
= "app id";
550 session_tab
.user_agent_override
= "override";
551 session_tab
.timestamp
= kTime5
;
552 session_tab
.navigations
.push_back(
553 SerializedNavigationEntryTestHelper::CreateNavigation(
554 "http://www.example.com", "Example"));
555 session_tab
.session_storage_persistent_id
= "persistent id";
556 manager()->SetSessionTabFromDelegate(tab
, kTime4
, &session_tab
);
558 EXPECT_EQ(0, session_tab
.window_id
.id());
559 EXPECT_EQ(0, session_tab
.tab_id
.id());
560 EXPECT_EQ(0, session_tab
.tab_visual_index
);
561 EXPECT_EQ(2, session_tab
.current_navigation_index
);
562 EXPECT_FALSE(session_tab
.pinned
);
563 EXPECT_TRUE(session_tab
.extension_app_id
.empty());
564 EXPECT_TRUE(session_tab
.user_agent_override
.empty());
565 EXPECT_EQ(kTime4
, session_tab
.timestamp
);
566 ASSERT_EQ(3u, session_tab
.navigations
.size());
567 EXPECT_EQ(url1
, session_tab
.navigations
[0].virtual_url());
568 EXPECT_EQ(url2
, session_tab
.navigations
[1].virtual_url());
569 EXPECT_EQ(url3
, session_tab
.navigations
[2].virtual_url());
570 EXPECT_EQ(kTime1
, session_tab
.navigations
[0].timestamp());
571 EXPECT_EQ(kTime2
, session_tab
.navigations
[1].timestamp());
572 EXPECT_EQ(kTime3
, session_tab
.navigations
[2].timestamp());
573 EXPECT_EQ(200, session_tab
.navigations
[0].http_status_code());
574 EXPECT_EQ(201, session_tab
.navigations
[1].http_status_code());
575 EXPECT_EQ(202, session_tab
.navigations
[2].http_status_code());
576 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID
,
577 session_tab
.navigations
[0].blocked_state());
578 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID
,
579 session_tab
.navigations
[1].blocked_state());
580 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID
,
581 session_tab
.navigations
[2].blocked_state());
582 EXPECT_TRUE(session_tab
.session_storage_persistent_id
.empty());
585 // Ensure the current_navigation_index gets set properly when the navigation
586 // stack gets trucated to +/- 6 entries.
587 TEST_F(SessionsSyncManagerTest
, SetSessionTabFromDelegateNavigationIndex
) {
588 SyncedTabDelegateFake tab
;
589 scoped_ptr
<content::NavigationEntry
> entry0(
590 content::NavigationEntry::Create());
591 GURL
url0("http://www.google.com/");
592 entry0
->SetVirtualURL(url0
);
593 entry0
->SetTimestamp(kTime0
);
594 entry0
->SetHttpStatusCode(200);
595 scoped_ptr
<content::NavigationEntry
> entry1(
596 content::NavigationEntry::Create());
597 GURL
url1("http://www.zoogle.com/");
598 entry1
->SetVirtualURL(url1
);
599 entry1
->SetTimestamp(kTime1
);
600 entry1
->SetHttpStatusCode(200);
601 scoped_ptr
<content::NavigationEntry
> entry2(
602 content::NavigationEntry::Create());
603 GURL
url2("http://www.noogle.com/");
604 entry2
->SetVirtualURL(url2
);
605 entry2
->SetTimestamp(kTime2
);
606 entry2
->SetHttpStatusCode(200);
607 scoped_ptr
<content::NavigationEntry
> entry3(
608 content::NavigationEntry::Create());
609 GURL
url3("http://www.doogle.com/");
610 entry3
->SetVirtualURL(url3
);
611 entry3
->SetTimestamp(kTime3
);
612 entry3
->SetHttpStatusCode(200);
613 scoped_ptr
<content::NavigationEntry
> entry4(
614 content::NavigationEntry::Create());
615 GURL
url4("http://www.yoogle.com/");
616 entry4
->SetVirtualURL(url4
);
617 entry4
->SetTimestamp(kTime4
);
618 entry4
->SetHttpStatusCode(200);
619 scoped_ptr
<content::NavigationEntry
> entry5(
620 content::NavigationEntry::Create());
621 GURL
url5("http://www.foogle.com/");
622 entry5
->SetVirtualURL(url5
);
623 entry5
->SetTimestamp(kTime5
);
624 entry5
->SetHttpStatusCode(200);
625 scoped_ptr
<content::NavigationEntry
> entry6(
626 content::NavigationEntry::Create());
627 GURL
url6("http://www.boogle.com/");
628 entry6
->SetVirtualURL(url6
);
629 entry6
->SetTimestamp(kTime6
);
630 entry6
->SetHttpStatusCode(200);
631 scoped_ptr
<content::NavigationEntry
> entry7(
632 content::NavigationEntry::Create());
633 GURL
url7("http://www.moogle.com/");
634 entry7
->SetVirtualURL(url7
);
635 entry7
->SetTimestamp(kTime7
);
636 entry7
->SetHttpStatusCode(200);
637 scoped_ptr
<content::NavigationEntry
> entry8(
638 content::NavigationEntry::Create());
639 GURL
url8("http://www.poogle.com/");
640 entry8
->SetVirtualURL(url8
);
641 entry8
->SetTimestamp(kTime8
);
642 entry8
->SetHttpStatusCode(200);
643 scoped_ptr
<content::NavigationEntry
> entry9(
644 content::NavigationEntry::Create());
645 GURL
url9("http://www.roogle.com/");
646 entry9
->SetVirtualURL(url9
);
647 entry9
->SetTimestamp(kTime9
);
648 entry9
->SetHttpStatusCode(200);
650 tab
.AppendEntry(entry0
.Pass());
651 tab
.AppendEntry(entry1
.Pass());
652 tab
.AppendEntry(entry2
.Pass());
653 tab
.AppendEntry(entry3
.Pass());
654 tab
.AppendEntry(entry4
.Pass());
655 tab
.AppendEntry(entry5
.Pass());
656 tab
.AppendEntry(entry6
.Pass());
657 tab
.AppendEntry(entry7
.Pass());
658 tab
.AppendEntry(entry8
.Pass());
659 tab
.AppendEntry(entry9
.Pass());
660 tab
.set_current_entry_index(8);
662 sessions::SessionTab session_tab
;
663 manager()->SetSessionTabFromDelegate(tab
, kTime9
, &session_tab
);
665 EXPECT_EQ(6, session_tab
.current_navigation_index
);
666 ASSERT_EQ(8u, session_tab
.navigations
.size());
667 EXPECT_EQ(url2
, session_tab
.navigations
[0].virtual_url());
668 EXPECT_EQ(url3
, session_tab
.navigations
[1].virtual_url());
669 EXPECT_EQ(url4
, session_tab
.navigations
[2].virtual_url());
672 // Ensure the current_navigation_index gets set to the end of the navigation
673 // stack if the current navigation is invalid.
674 TEST_F(SessionsSyncManagerTest
, SetSessionTabFromDelegateCurrentInvalid
) {
675 SyncedTabDelegateFake tab
;
676 scoped_ptr
<content::NavigationEntry
> entry0(
677 content::NavigationEntry::Create());
678 entry0
->SetVirtualURL(GURL("http://www.google.com"));
679 entry0
->SetTimestamp(kTime0
);
680 entry0
->SetHttpStatusCode(200);
681 scoped_ptr
<content::NavigationEntry
> entry1(
682 content::NavigationEntry::Create());
683 entry1
->SetVirtualURL(GURL(""));
684 entry1
->SetTimestamp(kTime1
);
685 entry1
->SetHttpStatusCode(200);
686 scoped_ptr
<content::NavigationEntry
> entry2(
687 content::NavigationEntry::Create());
688 entry2
->SetVirtualURL(GURL("http://www.noogle.com"));
689 entry2
->SetTimestamp(kTime2
);
690 entry2
->SetHttpStatusCode(200);
691 scoped_ptr
<content::NavigationEntry
> entry3(
692 content::NavigationEntry::Create());
693 entry3
->SetVirtualURL(GURL("http://www.doogle.com"));
694 entry3
->SetTimestamp(kTime3
);
695 entry3
->SetHttpStatusCode(200);
697 tab
.AppendEntry(entry0
.Pass());
698 tab
.AppendEntry(entry1
.Pass());
699 tab
.AppendEntry(entry2
.Pass());
700 tab
.AppendEntry(entry3
.Pass());
701 tab
.set_current_entry_index(1);
703 sessions::SessionTab session_tab
;
704 manager()->SetSessionTabFromDelegate(tab
, kTime9
, &session_tab
);
706 EXPECT_EQ(2, session_tab
.current_navigation_index
);
707 ASSERT_EQ(3u, session_tab
.navigations
.size());
710 // Tests that variation ids are set correctly.
711 TEST_F(SessionsSyncManagerTest
, SetVariationIds
) {
712 // Create two trials with a group which has a variation id for Chrome Sync
713 // and one with a variation id for another service.
714 const variations::VariationID kVariationId1
= 3300200;
715 const variations::VariationID kVariationId2
= 3300300;
716 const variations::VariationID kVariationId3
= 3300400;
718 base::FieldTrialList
field_trial_list(NULL
);
719 CreateAndActivateFieldTrial("trial name 1", "group name", kVariationId1
,
720 variations::CHROME_SYNC_SERVICE
);
721 CreateAndActivateFieldTrial("trial name 2", "group name", kVariationId2
,
722 variations::CHROME_SYNC_SERVICE
);
723 CreateAndActivateFieldTrial("trial name 3", "group name", kVariationId3
,
724 variations::GOOGLE_UPDATE_SERVICE
);
726 sessions::SessionTab session_tab
;
727 manager()->SetVariationIds(&session_tab
);
729 ASSERT_EQ(2u, session_tab
.variation_ids
.size());
730 EXPECT_EQ(kVariationId1
, session_tab
.variation_ids
[0]);
731 EXPECT_EQ(kVariationId2
, session_tab
.variation_ids
[1]);
734 // Tests that for supervised users blocked navigations are recorded and marked
735 // as such, while regular navigations are marked as allowed.
736 TEST_F(SessionsSyncManagerTest
, BlockedNavigations
) {
737 SyncedTabDelegateFake tab
;
738 scoped_ptr
<content::NavigationEntry
> entry1(
739 content::NavigationEntry::Create());
740 GURL
url1("http://www.google.com/");
741 entry1
->SetVirtualURL(url1
);
742 entry1
->SetTimestamp(kTime1
);
743 tab
.AppendEntry(entry1
.Pass());
745 scoped_ptr
<content::NavigationEntry
> entry2(
746 content::NavigationEntry::Create());
747 GURL
url2("http://blocked.com/foo");
748 entry2
->SetVirtualURL(url2
);
749 entry2
->SetTimestamp(kTime2
);
750 scoped_ptr
<content::NavigationEntry
> entry3(
751 content::NavigationEntry::Create());
752 GURL
url3("http://evil.com/");
753 entry3
->SetVirtualURL(url3
);
754 entry3
->SetTimestamp(kTime3
);
755 ScopedVector
<const content::NavigationEntry
> blocked_navigations
;
756 blocked_navigations
.push_back(entry2
.Pass());
757 blocked_navigations
.push_back(entry3
.Pass());
759 tab
.set_is_supervised(true);
760 tab
.set_blocked_navigations(&blocked_navigations
.get());
762 sessions::SessionTab session_tab
;
763 session_tab
.window_id
.set_id(1);
764 session_tab
.tab_id
.set_id(1);
765 session_tab
.tab_visual_index
= 1;
766 session_tab
.current_navigation_index
= 1;
767 session_tab
.pinned
= true;
768 session_tab
.extension_app_id
= "app id";
769 session_tab
.user_agent_override
= "override";
770 session_tab
.timestamp
= kTime5
;
771 session_tab
.navigations
.push_back(
772 SerializedNavigationEntryTestHelper::CreateNavigation(
773 "http://www.example.com", "Example"));
774 session_tab
.session_storage_persistent_id
= "persistent id";
775 manager()->SetSessionTabFromDelegate(tab
, kTime4
, &session_tab
);
777 EXPECT_EQ(0, session_tab
.window_id
.id());
778 EXPECT_EQ(0, session_tab
.tab_id
.id());
779 EXPECT_EQ(0, session_tab
.tab_visual_index
);
780 EXPECT_EQ(0, session_tab
.current_navigation_index
);
781 EXPECT_FALSE(session_tab
.pinned
);
782 EXPECT_TRUE(session_tab
.extension_app_id
.empty());
783 EXPECT_TRUE(session_tab
.user_agent_override
.empty());
784 EXPECT_EQ(kTime4
, session_tab
.timestamp
);
785 ASSERT_EQ(3u, session_tab
.navigations
.size());
786 EXPECT_EQ(url1
, session_tab
.navigations
[0].virtual_url());
787 EXPECT_EQ(url2
, session_tab
.navigations
[1].virtual_url());
788 EXPECT_EQ(url3
, session_tab
.navigations
[2].virtual_url());
789 EXPECT_EQ(kTime1
, session_tab
.navigations
[0].timestamp());
790 EXPECT_EQ(kTime2
, session_tab
.navigations
[1].timestamp());
791 EXPECT_EQ(kTime3
, session_tab
.navigations
[2].timestamp());
792 EXPECT_EQ(SerializedNavigationEntry::STATE_ALLOWED
,
793 session_tab
.navigations
[0].blocked_state());
794 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED
,
795 session_tab
.navigations
[1].blocked_state());
796 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED
,
797 session_tab
.navigations
[2].blocked_state());
798 EXPECT_TRUE(session_tab
.session_storage_persistent_id
.empty());
801 // Tests that the local session header objects is created properly in
802 // presence of no other session activity, once and only once.
803 TEST_F(SessionsSyncManagerTest
, MergeLocalSessionNoTabs
) {
804 syncer::SyncChangeList out
;
805 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
806 EXPECT_FALSE(manager()->current_machine_tag().empty());
808 EXPECT_EQ(2U, out
.size());
809 EXPECT_TRUE(out
[0].IsValid());
810 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[0].change_type());
811 const SyncData
data(out
[0].sync_data());
812 EXPECT_EQ(manager()->current_machine_tag(),
813 syncer::SyncDataLocal(data
).GetTag());
814 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
815 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
816 EXPECT_TRUE(specifics
.has_header());
817 const sync_pb::SessionHeader
& header_s
= specifics
.header();
818 EXPECT_TRUE(header_s
.has_device_type());
819 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s
.client_name());
820 EXPECT_EQ(0, header_s
.window_size());
822 EXPECT_TRUE(out
[1].IsValid());
823 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[1].change_type());
824 const SyncData
data_2(out
[1].sync_data());
825 EXPECT_EQ(manager()->current_machine_tag(),
826 syncer::SyncDataLocal(data_2
).GetTag());
827 const sync_pb::SessionSpecifics
& specifics2(data_2
.GetSpecifics().session());
828 EXPECT_EQ(manager()->current_machine_tag(), specifics2
.session_tag());
829 EXPECT_TRUE(specifics2
.has_header());
830 const sync_pb::SessionHeader
& header_s2
= specifics2
.header();
831 EXPECT_EQ(0, header_s2
.window_size());
833 // Now take that header node and feed it in as input.
834 SyncData
d(SyncData::CreateRemoteData(
838 syncer::AttachmentIdList(),
839 syncer::AttachmentServiceProxyForTest::Create()));
840 syncer::SyncDataList
in(&d
, &d
+ 1);
842 SessionsSyncManager
manager2(profile(), local_device(), NewDummyRouter());
843 syncer::SyncMergeResult result
= manager2
.MergeDataAndStartSyncing(
844 syncer::SESSIONS
, in
,
845 scoped_ptr
<syncer::SyncChangeProcessor
>(
846 new TestSyncProcessorStub(&out
)),
847 scoped_ptr
<syncer::SyncErrorFactory
>(
848 new syncer::SyncErrorFactoryMock()));
849 ASSERT_FALSE(result
.error().IsSet());
851 EXPECT_EQ(1U, out
.size());
852 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[0].change_type());
853 EXPECT_TRUE(out
[0].sync_data().GetSpecifics().session().has_header());
856 // Ensure model association associates the pre-existing tabs.
857 TEST_F(SessionsSyncManagerTest
, SwappedOutOnRestore
) {
858 AddTab(browser(), GURL("http://foo1"));
859 NavigateAndCommitActiveTab(GURL("http://foo2"));
860 AddTab(browser(), GURL("http://bar1"));
861 NavigateAndCommitActiveTab(GURL("http://bar2"));
862 AddTab(browser(), GURL("http://baz1"));
863 NavigateAndCommitActiveTab(GURL("http://baz2"));
864 const int kRestoredTabId
= 1337;
865 const int kNewTabId
= 2468;
867 syncer::SyncDataList in
;
868 syncer::SyncChangeList out
;
869 InitWithSyncDataTakeOutput(in
, &out
);
871 // Should be one header add, 3 tab add/update pairs, one header update.
872 ASSERT_EQ(8U, out
.size());
874 // For input, we set up:
875 // * one "normal" fully loaded tab
876 // * one "frozen" tab with no WebContents and a tab_id change
877 // * one "frozen" tab with no WebContents and no tab_id change
878 SyncData
t0(SyncData::CreateRemoteData(
880 out
[2].sync_data().GetSpecifics(),
882 syncer::AttachmentIdList(),
883 syncer::AttachmentServiceProxyForTest::Create()));
884 sync_pb::EntitySpecifics
entity(out
[4].sync_data().GetSpecifics());
885 entity
.mutable_session()->mutable_tab()->set_tab_id(kRestoredTabId
);
886 SyncData
t1(SyncData::CreateRemoteData(
890 syncer::AttachmentIdList(),
891 syncer::AttachmentServiceProxyForTest::Create()));
892 SyncData
t2(SyncData::CreateRemoteData(
894 out
[6].sync_data().GetSpecifics(),
896 syncer::AttachmentIdList(),
897 syncer::AttachmentServiceProxyForTest::Create()));
902 manager()->StopSyncing(syncer::SESSIONS
);
904 const std::set
<const SyncedWindowDelegate
*>& windows
=
905 SyncedWindowDelegate::GetAll();
906 ASSERT_EQ(1U, windows
.size());
907 SyncedTabDelegateFake t1_override
, t2_override
;
908 t1_override
.SetSyncId(1); // No WebContents by default.
909 t2_override
.SetSyncId(2); // No WebContents by default.
910 SyncedWindowDelegateOverride
window_override(*windows
.begin());
911 window_override
.OverrideTabAt(1, &t1_override
, kNewTabId
);
912 window_override
.OverrideTabAt(2, &t2_override
,
913 t2
.GetSpecifics().session().tab().tab_id());
914 std::set
<const SyncedWindowDelegate
*> delegates
;
915 delegates
.insert(&window_override
);
916 scoped_ptr
<TestSyncedWindowDelegatesGetter
> getter(
917 new TestSyncedWindowDelegatesGetter(delegates
));
918 manager()->synced_window_getter_
.reset(getter
.release());
920 syncer::SyncMergeResult result
= manager()->MergeDataAndStartSyncing(
921 syncer::SESSIONS
, in
,
922 scoped_ptr
<syncer::SyncChangeProcessor
>(
923 new TestSyncProcessorStub(&out
)),
924 scoped_ptr
<syncer::SyncErrorFactory
>(
925 new syncer::SyncErrorFactoryMock()));
927 // There should be two changes, one for the fully associated tab, and
928 // one for the tab_id update to t1. t2 shouldn't need to be updated.
929 ASSERT_EQ(2U, FilterOutLocalHeaderChanges(&out
)->size());
930 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[0].change_type());
931 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[1].change_type());
933 out
[1].sync_data().GetSpecifics().session().tab().tab_id());
936 SessionsSyncManager::TabLinksMap tab_map
= manager()->local_tab_map_
;
937 ASSERT_EQ(3U, tab_map
.size());
938 int t2_tab_id
= t2
.GetSpecifics().session().tab().tab_id();
939 EXPECT_EQ(2, tab_map
.find(t2_tab_id
)->second
->tab_node_id());
940 EXPECT_EQ(1, tab_map
.find(kNewTabId
)->second
->tab_node_id());
941 int t0_tab_id
= out
[0].sync_data().GetSpecifics().session().tab().tab_id();
942 EXPECT_EQ(0, tab_map
.find(t0_tab_id
)->second
->tab_node_id());
943 // TODO(tim): Once bug 337057 is fixed, we can issue an OnLocalTabModified
944 // from here (using an override similar to above) to return a new tab id
945 // and verify that we don't see any node creations in the SyncChangeProcessor
946 // (similar to how SessionsSyncManagerTest.OnLocalTabModified works.)
949 // Tests MergeDataAndStartSyncing with sync data but no local data.
950 TEST_F(SessionsSyncManagerTest
, MergeWithInitialForeignSession
) {
951 std::string tag
= "tag1";
953 SessionID::id_type n1
[] = {5, 10, 13, 17};
954 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
955 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
956 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
957 tag
, tab_list1
, &tabs1
));
958 // Add a second window.
959 SessionID::id_type n2
[] = {7, 15, 18, 20};
960 std::vector
<SessionID::id_type
> tab_list2(n2
, n2
+ arraysize(n2
));
961 helper()->AddWindowSpecifics(1, tab_list2
, &meta
);
963 // Set up initial data.
964 syncer::SyncDataList initial_data
;
965 sync_pb::EntitySpecifics entity
;
966 entity
.mutable_session()->CopyFrom(meta
);
967 initial_data
.push_back(SyncData::CreateRemoteData(
971 syncer::AttachmentIdList(),
972 syncer::AttachmentServiceProxyForTest::Create()));
973 AddTabsToSyncDataList(tabs1
, &initial_data
);
975 for (size_t i
= 0; i
< tab_list2
.size(); ++i
) {
976 sync_pb::EntitySpecifics entity
;
977 helper()->BuildTabSpecifics(tag
, 0, tab_list2
[i
],
978 entity
.mutable_session());
979 initial_data
.push_back(SyncData::CreateRemoteData(
983 syncer::AttachmentIdList(),
984 syncer::AttachmentServiceProxyForTest::Create()));
987 syncer::SyncChangeList output
;
988 InitWithSyncDataTakeOutput(initial_data
, &output
);
989 EXPECT_TRUE(FilterOutLocalHeaderChanges(&output
)->empty());
991 std::vector
<const SyncedSession
*> foreign_sessions
;
992 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
993 ASSERT_EQ(1U, foreign_sessions
.size());
994 std::vector
<std::vector
<SessionID::id_type
> > session_reference
;
995 session_reference
.push_back(tab_list1
);
996 session_reference
.push_back(tab_list2
);
997 helper()->VerifySyncedSession(tag
, session_reference
, *(foreign_sessions
[0]));
1000 // This is a combination of MergeWithInitialForeignSession and
1001 // MergeLocalSessionExistingTabs. We repeat some checks performed in each of
1002 // those tests to ensure the common mixed scenario works.
1003 TEST_F(SessionsSyncManagerTest
, MergeWithLocalAndForeignTabs
) {
1005 AddTab(browser(), GURL("http://foo1"));
1006 NavigateAndCommitActiveTab(GURL("http://foo2"));
1009 std::string tag
= "tag1";
1010 SessionID::id_type n1
[] = {5, 10, 13, 17};
1011 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
1012 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1013 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1014 tag
, tab_list1
, &tabs1
));
1015 syncer::SyncDataList foreign_data
;
1016 sync_pb::EntitySpecifics entity
;
1017 entity
.mutable_session()->CopyFrom(meta
);
1018 foreign_data
.push_back(SyncData::CreateRemoteData(
1022 syncer::AttachmentIdList(),
1023 syncer::AttachmentServiceProxyForTest::Create()));
1024 AddTabsToSyncDataList(tabs1
, &foreign_data
);
1026 syncer::SyncChangeList output
;
1027 InitWithSyncDataTakeOutput(foreign_data
, &output
);
1028 ASSERT_EQ(4U, output
.size());
1030 // Verify the local header.
1031 EXPECT_TRUE(output
[0].IsValid());
1032 EXPECT_EQ(SyncChange::ACTION_ADD
, output
[0].change_type());
1033 const SyncData
data(output
[0].sync_data());
1034 EXPECT_EQ(manager()->current_machine_tag(),
1035 syncer::SyncDataLocal(data
).GetTag());
1036 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1037 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1038 EXPECT_TRUE(specifics
.has_header());
1039 const sync_pb::SessionHeader
& header_s
= specifics
.header();
1040 EXPECT_TRUE(header_s
.has_device_type());
1041 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s
.client_name());
1042 EXPECT_EQ(0, header_s
.window_size());
1044 // Verify the tab node creations and updates with content.
1045 for (int i
= 1; i
< 3; i
++) {
1046 EXPECT_TRUE(output
[i
].IsValid());
1047 const SyncData
data(output
[i
].sync_data());
1048 EXPECT_TRUE(base::StartsWith(syncer::SyncDataLocal(data
).GetTag(),
1049 manager()->current_machine_tag(),
1050 base::CompareCase::SENSITIVE
));
1051 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1052 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1054 EXPECT_EQ(SyncChange::ACTION_ADD
, output
[1].change_type());
1055 EXPECT_EQ(SyncChange::ACTION_UPDATE
, output
[2].change_type());
1056 EXPECT_TRUE(output
[2].sync_data().GetSpecifics().session().has_tab());
1058 // Verify the header was updated to reflect window state.
1059 EXPECT_TRUE(output
[3].IsValid());
1060 EXPECT_EQ(SyncChange::ACTION_UPDATE
, output
[3].change_type());
1061 const SyncData
data_2(output
[3].sync_data());
1062 EXPECT_EQ(manager()->current_machine_tag(),
1063 syncer::SyncDataLocal(data_2
).GetTag());
1064 const sync_pb::SessionSpecifics
& specifics2(data_2
.GetSpecifics().session());
1065 EXPECT_EQ(manager()->current_machine_tag(), specifics2
.session_tag());
1066 EXPECT_TRUE(specifics2
.has_header());
1067 const sync_pb::SessionHeader
& header_s2
= specifics2
.header();
1068 EXPECT_EQ(1, header_s2
.window_size());
1070 // Verify foreign data.
1071 std::vector
<const SyncedSession
*> foreign_sessions
;
1072 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1073 std::vector
<std::vector
<SessionID::id_type
> > session_reference
;
1074 session_reference
.push_back(tab_list1
);
1075 helper()->VerifySyncedSession(tag
, session_reference
, *(foreign_sessions
[0]));
1076 // There should be one and only one foreign session. If VerifySyncedSession
1077 // was successful above this EXPECT call ensures the local session didn't
1078 // get mistakenly added to foreign tracking (Similar to ExistingTabs test).
1079 EXPECT_EQ(1U, foreign_sessions
.size());
1082 // Tests the common scenario. Merge with both local and foreign session data
1083 // followed by updates flowing from sync and local.
1084 TEST_F(SessionsSyncManagerTest
, UpdatesAfterMixedMerge
) {
1085 // Add local and foreign data.
1086 AddTab(browser(), GURL("http://foo1"));
1087 NavigateAndCommitActiveTab(GURL("http://foo2"));
1089 std::string tag1
= "tag1";
1090 syncer::SyncDataList foreign_data1
;
1091 std::vector
<std::vector
<SessionID::id_type
> > meta1_reference
;
1092 sync_pb::SessionSpecifics meta1
;
1094 SessionID::id_type n1
[] = {5, 10, 13, 17};
1095 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
1096 meta1_reference
.push_back(tab_list1
);
1097 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1098 meta1
= helper()->BuildForeignSession(tag1
, tab_list1
, &tabs1
);
1099 sync_pb::EntitySpecifics entity
;
1100 entity
.mutable_session()->CopyFrom(meta1
);
1101 foreign_data1
.push_back(SyncData::CreateRemoteData(
1105 syncer::AttachmentIdList(),
1106 syncer::AttachmentServiceProxyForTest::Create()));
1107 AddTabsToSyncDataList(tabs1
, &foreign_data1
);
1109 syncer::SyncChangeList output1
;
1110 InitWithSyncDataTakeOutput(foreign_data1
, &output1
);
1111 ASSERT_EQ(4U, output1
.size());
1113 // Add a second window to the foreign session.
1114 // TODO(tim): Bug 98892. Add local window too when observers are hooked up.
1115 SessionID::id_type tab_nums2
[] = {7, 15, 18, 20};
1116 std::vector
<SessionID::id_type
> tab_list2(
1117 tab_nums2
, tab_nums2
+ arraysize(tab_nums2
));
1118 meta1_reference
.push_back(tab_list2
);
1119 helper()->AddWindowSpecifics(1, tab_list2
, &meta1
);
1120 std::vector
<sync_pb::SessionSpecifics
> tabs2
;
1121 tabs2
.resize(tab_list2
.size());
1122 for (size_t i
= 0; i
< tab_list2
.size(); ++i
) {
1123 helper()->BuildTabSpecifics(tag1
, 0, tab_list2
[i
], &tabs2
[i
]);
1126 syncer::SyncChangeList changes
;
1127 changes
.push_back(MakeRemoteChange(1, meta1
, SyncChange::ACTION_UPDATE
));
1128 AddTabsToChangeList(tabs2
, SyncChange::ACTION_ADD
, &changes
);
1129 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1132 // Check that the foreign session was associated and retrieve the data.
1133 std::vector
<const SyncedSession
*> foreign_sessions
;
1134 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1135 ASSERT_EQ(1U, foreign_sessions
.size());
1136 ASSERT_EQ(4U, foreign_sessions
[0]->windows
.find(0)->second
->tabs
.size());
1137 ASSERT_EQ(4U, foreign_sessions
[0]->windows
.find(1)->second
->tabs
.size());
1138 helper()->VerifySyncedSession(tag1
, meta1_reference
, *(foreign_sessions
[0]));
1140 // Add a new foreign session.
1141 std::string tag2
= "tag2";
1142 SessionID::id_type n2
[] = {107, 115};
1143 std::vector
<SessionID::id_type
> tag2_tab_list(n2
, n2
+ arraysize(n2
));
1144 std::vector
<sync_pb::SessionSpecifics
> tag2_tabs
;
1145 sync_pb::SessionSpecifics
meta2(helper()->BuildForeignSession(
1146 tag2
, tag2_tab_list
, &tag2_tabs
));
1147 changes
.push_back(MakeRemoteChange(100, meta2
, SyncChange::ACTION_ADD
));
1148 AddTabsToChangeList(tag2_tabs
, SyncChange::ACTION_ADD
, &changes
);
1150 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1153 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1154 std::vector
<std::vector
<SessionID::id_type
> > meta2_reference
;
1155 meta2_reference
.push_back(tag2_tab_list
);
1156 ASSERT_EQ(2U, foreign_sessions
.size());
1157 ASSERT_EQ(2U, foreign_sessions
[1]->windows
.find(0)->second
->tabs
.size());
1158 helper()->VerifySyncedSession(tag2
, meta2_reference
, *(foreign_sessions
[1]));
1159 foreign_sessions
.clear();
1161 // Remove a tab from a window.
1162 meta1_reference
[0].pop_back();
1163 tab_list1
.pop_back();
1164 sync_pb::SessionWindow
* win
= meta1
.mutable_header()->mutable_window(0);
1166 for (std::vector
<int>::const_iterator iter
= tab_list1
.begin();
1167 iter
!= tab_list1
.end(); ++iter
) {
1168 win
->add_tab(*iter
);
1170 syncer::SyncChangeList removal
;
1171 removal
.push_back(MakeRemoteChange(1, meta1
, SyncChange::ACTION_UPDATE
));
1172 AddTabsToChangeList(tabs1
, SyncChange::ACTION_UPDATE
, &removal
);
1173 manager()->ProcessSyncChanges(FROM_HERE
, removal
);
1175 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1176 ASSERT_EQ(2U, foreign_sessions
.size());
1177 ASSERT_EQ(3U, foreign_sessions
[0]->windows
.find(0)->second
->tabs
.size());
1178 helper()->VerifySyncedSession(tag1
, meta1_reference
, *(foreign_sessions
[0]));
1181 // Tests that this SyncSessionManager knows how to delete foreign sessions
1183 TEST_F(SessionsSyncManagerTest
, DeleteForeignSession
) {
1184 InitWithNoSyncData();
1185 std::string tag
= "tag1";
1186 syncer::SyncChangeList changes
;
1188 std::vector
<const SyncedSession
*> foreign_sessions
;
1189 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions
));
1190 manager()->DeleteForeignSessionInternal(tag
, &changes
);
1191 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions
));
1192 EXPECT_TRUE(changes
.empty());
1194 // Fill an instance of session specifics with a foreign session's data.
1195 std::vector
<sync_pb::SessionSpecifics
> tabs
;
1196 SessionID::id_type n1
[] = {5, 10, 13, 17};
1197 std::vector
<SessionID::id_type
> tab_nums1(n1
, n1
+ arraysize(n1
));
1198 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1199 tag
, tab_nums1
, &tabs
));
1201 // Update associator with the session's meta node, window, and tabs.
1202 manager()->UpdateTrackerWithForeignSession(meta
, base::Time());
1203 for (std::vector
<sync_pb::SessionSpecifics
>::iterator iter
= tabs
.begin();
1204 iter
!= tabs
.end(); ++iter
) {
1205 manager()->UpdateTrackerWithForeignSession(*iter
, base::Time());
1207 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1208 ASSERT_EQ(1U, foreign_sessions
.size());
1210 // Now delete the foreign session.
1211 manager()->DeleteForeignSessionInternal(tag
, &changes
);
1212 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions
));
1214 EXPECT_EQ(5U, changes
.size());
1215 std::set
<std::string
> expected_tags(&tag
, &tag
+ 1);
1216 for (int i
= 0; i
< 5; i
++)
1217 expected_tags
.insert(TabNodePool::TabIdToTag(tag
, i
));
1219 for (int i
= 0; i
< 5; i
++) {
1220 SCOPED_TRACE(changes
[i
].ToString());
1221 EXPECT_TRUE(changes
[i
].IsValid());
1222 EXPECT_EQ(SyncChange::ACTION_DELETE
, changes
[i
].change_type());
1223 EXPECT_TRUE(changes
[i
].sync_data().IsValid());
1225 expected_tags
.erase(
1226 syncer::SyncDataLocal(changes
[i
].sync_data()).GetTag()));
1230 // Write a foreign session to a node, with the tabs arriving first, and then
1232 TEST_F(SessionsSyncManagerTest
, WriteForeignSessionToNodeTabsFirst
) {
1233 InitWithNoSyncData();
1235 // Fill an instance of session specifics with a foreign session's data.
1236 std::string tag
= "tag1";
1237 SessionID::id_type nums1
[] = {5, 10, 13, 17};
1238 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1239 std::vector
<SessionID::id_type
> tab_list1(nums1
, nums1
+ arraysize(nums1
));
1240 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1241 tag
, tab_list1
, &tabs1
));
1243 syncer::SyncChangeList adds
;
1244 // Add tabs for first window, then the meta node.
1245 AddTabsToChangeList(tabs1
, SyncChange::ACTION_ADD
, &adds
);
1246 adds
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_ADD
));
1247 manager()->ProcessSyncChanges(FROM_HERE
, adds
);
1249 // Check that the foreign session was associated and retrieve the data.
1250 std::vector
<const SyncedSession
*> foreign_sessions
;
1251 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1252 ASSERT_EQ(1U, foreign_sessions
.size());
1253 std::vector
<std::vector
<SessionID::id_type
> > session_reference
;
1254 session_reference
.push_back(tab_list1
);
1255 helper()->VerifySyncedSession(tag
, session_reference
, *(foreign_sessions
[0]));
1258 // Write a foreign session to a node with some tabs that never arrive.
1259 TEST_F(SessionsSyncManagerTest
, WriteForeignSessionToNodeMissingTabs
) {
1260 InitWithNoSyncData();
1262 // Fill an instance of session specifics with a foreign session's data.
1263 std::string tag
= "tag1";
1264 SessionID::id_type nums1
[] = {5, 10, 13, 17};
1265 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1266 std::vector
<SessionID::id_type
> tab_list1(nums1
, nums1
+ arraysize(nums1
));
1267 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1268 tag
, tab_list1
, &tabs1
));
1269 // Add a second window, but this time only create two tab nodes, despite the
1270 // window expecting four tabs.
1271 SessionID::id_type tab_nums2
[] = {7, 15, 18, 20};
1272 std::vector
<SessionID::id_type
> tab_list2(
1273 tab_nums2
, tab_nums2
+ arraysize(tab_nums2
));
1274 helper()->AddWindowSpecifics(1, tab_list2
, &meta
);
1275 std::vector
<sync_pb::SessionSpecifics
> tabs2
;
1277 for (size_t i
= 0; i
< 2; ++i
) {
1278 helper()->BuildTabSpecifics(tag
, 0, tab_list2
[i
], &tabs2
[i
]);
1281 syncer::SyncChangeList changes
;
1282 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_ADD
));
1283 AddTabsToChangeList(tabs1
, SyncChange::ACTION_ADD
, &changes
);
1284 AddTabsToChangeList(tabs2
, SyncChange::ACTION_ADD
, &changes
);
1285 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1288 // Check that the foreign session was associated and retrieve the data.
1289 std::vector
<const SyncedSession
*> foreign_sessions
;
1290 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1291 ASSERT_EQ(1U, foreign_sessions
.size());
1292 ASSERT_EQ(2U, foreign_sessions
[0]->windows
.size());
1293 ASSERT_EQ(4U, foreign_sessions
[0]->windows
.find(0)->second
->tabs
.size());
1294 ASSERT_EQ(4U, foreign_sessions
[0]->windows
.find(1)->second
->tabs
.size());
1296 // Close the second window.
1297 meta
.mutable_header()->clear_window();
1298 helper()->AddWindowSpecifics(0, tab_list1
, &meta
);
1299 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_UPDATE
));
1300 // Update associator with the session's meta node containing one window.
1301 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1303 // Check that the foreign session was associated and retrieve the data.
1304 foreign_sessions
.clear();
1305 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1306 ASSERT_EQ(1U, foreign_sessions
.size());
1307 ASSERT_EQ(1U, foreign_sessions
[0]->windows
.size());
1308 std::vector
<std::vector
<SessionID::id_type
> > session_reference
;
1309 session_reference
.push_back(tab_list1
);
1310 helper()->VerifySyncedSession(tag
, session_reference
, *(foreign_sessions
[0]));
1313 // Tests that the SessionsSyncManager can handle a remote client deleting
1314 // sync nodes that belong to this local session.
1315 TEST_F(SessionsSyncManagerTest
, ProcessRemoteDeleteOfLocalSession
) {
1316 syncer::SyncChangeList out
;
1317 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
1318 ASSERT_EQ(2U, out
.size());
1319 sync_pb::EntitySpecifics
entity(out
[0].sync_data().GetSpecifics());
1320 SyncData
d(SyncData::CreateRemoteData(
1324 syncer::AttachmentIdList(),
1325 syncer::AttachmentServiceProxyForTest::Create()));
1326 SetSyncData(syncer::SyncDataList(&d
, &d
+ 1));
1329 syncer::SyncChangeList changes
;
1331 MakeRemoteChange(1, entity
.session(), SyncChange::ACTION_DELETE
));
1332 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1333 EXPECT_TRUE(manager()->local_tab_pool_out_of_sync_
);
1334 EXPECT_TRUE(out
.empty()); // ChangeProcessor shouldn't see any activity.
1336 // This should trigger repair of the TabNodePool.
1337 const GURL
foo1("http://foo/1");
1338 AddTab(browser(), foo1
);
1339 EXPECT_FALSE(manager()->local_tab_pool_out_of_sync_
);
1341 // AddTab triggers two notifications, one for the tab insertion and one for
1342 // committing the NavigationEntry. The first notification results in a tab
1343 // we don't associate although we do update the header node. The second
1344 // notification triggers association of the tab, and the subsequent window
1345 // update. So we should see 4 changes at the SyncChangeProcessor.
1346 ASSERT_EQ(4U, out
.size());
1348 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[0].change_type());
1349 ASSERT_TRUE(out
[0].sync_data().GetSpecifics().session().has_header());
1350 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[1].change_type());
1351 int tab_node_id
= out
[1].sync_data().GetSpecifics().session().tab_node_id();
1352 EXPECT_EQ(TabNodePool::TabIdToTag(
1353 manager()->current_machine_tag(), tab_node_id
),
1354 syncer::SyncDataLocal(out
[1].sync_data()).GetTag());
1355 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[2].change_type());
1356 ASSERT_TRUE(out
[2].sync_data().GetSpecifics().session().has_tab());
1357 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[3].change_type());
1358 ASSERT_TRUE(out
[3].sync_data().GetSpecifics().session().has_header());
1360 // Verify the actual content.
1361 const sync_pb::SessionHeader
& session_header
=
1362 out
[3].sync_data().GetSpecifics().session().header();
1363 ASSERT_EQ(1, session_header
.window_size());
1364 EXPECT_EQ(1, session_header
.window(0).tab_size());
1365 const sync_pb::SessionTab
& tab1
=
1366 out
[2].sync_data().GetSpecifics().session().tab();
1367 ASSERT_EQ(1, tab1
.navigation_size());
1368 EXPECT_EQ(foo1
.spec(), tab1
.navigation(0).virtual_url());
1370 // Verify TabNodePool integrity.
1371 EXPECT_EQ(1U, manager()->local_tab_pool_
.Capacity());
1372 EXPECT_TRUE(manager()->local_tab_pool_
.Empty());
1375 SessionsSyncManager::TabLinksMap tab_map
= manager()->local_tab_map_
;
1376 ASSERT_EQ(1U, tab_map
.size());
1377 int tab_id
= out
[2].sync_data().GetSpecifics().session().tab().tab_id();
1378 EXPECT_EQ(tab_node_id
, tab_map
.find(tab_id
)->second
->tab_node_id());
1381 // Test that receiving a session delete from sync removes the session
1383 TEST_F(SessionsSyncManagerTest
, ProcessForeignDelete
) {
1384 InitWithNoSyncData();
1385 SessionID::id_type n
[] = {5};
1386 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1387 std::vector
<SessionID::id_type
> tab_list(n
, n
+ arraysize(n
));
1388 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1389 "tag1", tab_list
, &tabs1
));
1391 syncer::SyncChangeList changes
;
1392 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_ADD
));
1393 AddTabsToChangeList(tabs1
, SyncChange::ACTION_ADD
, &changes
);
1394 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1396 std::vector
<const SyncedSession
*> foreign_sessions
;
1397 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1398 ASSERT_EQ(1U, foreign_sessions
.size());
1401 foreign_sessions
.clear();
1402 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_DELETE
));
1403 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1405 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions
));
1408 // TODO(shashishekhar): "Move this to TabNodePool unittests."
1409 TEST_F(SessionsSyncManagerTest
, SaveUnassociatedNodesForReassociation
) {
1410 syncer::SyncChangeList changes
;
1411 InitWithNoSyncData();
1413 std::string local_tag
= manager()->current_machine_tag();
1414 // Create a free node and then dissassociate sessions so that it ends up
1416 manager()->local_tab_pool_
.GetFreeTabNode(&changes
);
1418 // Update the tab_id of the node, so that it is considered a valid
1419 // unassociated node otherwise it will be mistaken for a corrupted node and
1420 // will be deleted before being added to the tab node pool.
1421 sync_pb::EntitySpecifics
entity(changes
[0].sync_data().GetSpecifics());
1422 entity
.mutable_session()->mutable_tab()->set_tab_id(1);
1423 SyncData
d(SyncData::CreateRemoteData(
1427 syncer::AttachmentIdList(),
1428 syncer::AttachmentServiceProxyForTest::Create()));
1429 syncer::SyncDataList
in(&d
, &d
+ 1);
1431 SessionsSyncManager
manager2(profile(), local_device(), NewDummyRouter());
1432 syncer::SyncMergeResult result
= manager2
.MergeDataAndStartSyncing(
1433 syncer::SESSIONS
, in
,
1434 scoped_ptr
<syncer::SyncChangeProcessor
>(
1435 new TestSyncProcessorStub(&changes
)),
1436 scoped_ptr
<syncer::SyncErrorFactory
>(
1437 new syncer::SyncErrorFactoryMock()));
1438 ASSERT_FALSE(result
.error().IsSet());
1439 EXPECT_TRUE(FilterOutLocalHeaderChanges(&changes
)->empty());
1442 TEST_F(SessionsSyncManagerTest
, MergeDeletesCorruptNode
) {
1443 syncer::SyncChangeList changes
;
1444 InitWithNoSyncData();
1446 std::string local_tag
= manager()->current_machine_tag();
1447 int tab_node_id
= manager()->local_tab_pool_
.GetFreeTabNode(&changes
);
1448 SyncData
d(SyncData::CreateRemoteData(
1450 changes
[0].sync_data().GetSpecifics(),
1452 syncer::AttachmentIdList(),
1453 syncer::AttachmentServiceProxyForTest::Create()));
1454 syncer::SyncDataList
in(&d
, &d
+ 1);
1458 InitWithSyncDataTakeOutput(in
, &changes
);
1459 EXPECT_EQ(1U, FilterOutLocalHeaderChanges(&changes
)->size());
1460 EXPECT_EQ(SyncChange::ACTION_DELETE
, changes
[0].change_type());
1461 EXPECT_EQ(TabNodePool::TabIdToTag(local_tag
, tab_node_id
),
1462 syncer::SyncDataLocal(changes
[0].sync_data()).GetTag());
1465 // Test that things work if a tab is initially ignored.
1466 TEST_F(SessionsSyncManagerTest
, AssociateWindowsDontReloadTabs
) {
1467 syncer::SyncChangeList out
;
1468 // Go to a URL that is ignored by session syncing.
1469 AddTab(browser(), GURL("chrome://preferences/"));
1470 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
1471 ASSERT_EQ(2U, out
.size()); // Header add and update.
1474 out
[1].sync_data().GetSpecifics().session().header().window_size());
1477 // Go to a sync-interesting URL.
1478 NavigateAndCommitActiveTab(GURL("http://foo2"));
1480 EXPECT_EQ(3U, out
.size()); // Tab add, update, and header update.
1483 base::StartsWith(syncer::SyncDataLocal(out
[0].sync_data()).GetTag(),
1484 manager()->current_machine_tag(),
1485 base::CompareCase::SENSITIVE
));
1486 EXPECT_EQ(manager()->current_machine_tag(),
1487 out
[0].sync_data().GetSpecifics().session().session_tag());
1488 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[0].change_type());
1491 base::StartsWith(syncer::SyncDataLocal(out
[1].sync_data()).GetTag(),
1492 manager()->current_machine_tag(),
1493 base::CompareCase::SENSITIVE
));
1494 EXPECT_EQ(manager()->current_machine_tag(),
1495 out
[1].sync_data().GetSpecifics().session().session_tag());
1496 EXPECT_TRUE(out
[1].sync_data().GetSpecifics().session().has_tab());
1497 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[1].change_type());
1499 EXPECT_TRUE(out
[2].IsValid());
1500 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[2].change_type());
1501 const SyncData
data(out
[2].sync_data());
1502 EXPECT_EQ(manager()->current_machine_tag(),
1503 syncer::SyncDataLocal(data
).GetTag());
1504 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1505 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1506 EXPECT_TRUE(specifics
.has_header());
1507 const sync_pb::SessionHeader
& header_s
= specifics
.header();
1508 EXPECT_EQ(1, header_s
.window_size());
1509 EXPECT_EQ(1, header_s
.window(0).tab_size());
1512 // Tests that the SyncSessionManager responds to local tab events properly.
1513 TEST_F(SessionsSyncManagerTest
, OnLocalTabModified
) {
1514 syncer::SyncChangeList out
;
1515 // Init with no local data, relies on MergeLocalSessionNoTabs.
1516 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
1517 ASSERT_FALSE(manager()->current_machine_tag().empty());
1518 ASSERT_EQ(2U, out
.size());
1520 // Copy the original header.
1521 sync_pb::EntitySpecifics
header(out
[0].sync_data().GetSpecifics());
1524 const GURL
foo1("http://foo/1");
1525 const GURL
foo2("http://foo/2");
1526 const GURL
bar1("http://bar/1");
1527 const GURL
bar2("http://bar/2");
1528 AddTab(browser(), foo1
);
1529 NavigateAndCommitActiveTab(foo2
);
1530 AddTab(browser(), bar1
);
1531 NavigateAndCommitActiveTab(bar2
);
1533 // One add, one update for each AddTab.
1534 // One update for each NavigateAndCommit.
1535 // = 6 total tab updates.
1536 // One header update corresponding to each of those.
1537 // = 6 total header updates.
1538 // 12 total updates.
1539 ASSERT_EQ(12U, out
.size());
1541 // Verify the tab node creations and updates to ensure the SyncProcessor
1542 // sees the right operations.
1543 for (int i
= 0; i
< 12; i
++) {
1545 EXPECT_TRUE(out
[i
].IsValid());
1546 const SyncData
data(out
[i
].sync_data());
1547 EXPECT_TRUE(base::StartsWith(syncer::SyncDataLocal(data
).GetTag(),
1548 manager()->current_machine_tag(),
1549 base::CompareCase::SENSITIVE
));
1550 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1551 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1553 // First thing on an AddTab is a no-op header update for parented tab.
1554 EXPECT_EQ(header
.SerializeAsString(),
1555 data
.GetSpecifics().SerializeAsString());
1556 EXPECT_EQ(manager()->current_machine_tag(),
1557 syncer::SyncDataLocal(data
).GetTag());
1558 } else if (i
% 6 == 1) {
1559 // Next, the TabNodePool should create the tab node.
1560 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[i
].change_type());
1561 EXPECT_EQ(TabNodePool::TabIdToTag(
1562 manager()->current_machine_tag(),
1563 data
.GetSpecifics().session().tab_node_id()),
1564 syncer::SyncDataLocal(data
).GetTag());
1565 } else if (i
% 6 == 2) {
1566 // Then we see the tab update to the URL.
1567 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[i
].change_type());
1568 EXPECT_EQ(TabNodePool::TabIdToTag(
1569 manager()->current_machine_tag(),
1570 data
.GetSpecifics().session().tab_node_id()),
1571 syncer::SyncDataLocal(data
).GetTag());
1572 ASSERT_TRUE(specifics
.has_tab());
1573 } else if (i
% 6 == 3) {
1574 // The header needs to be updated to reflect the new window state.
1575 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[i
].change_type());
1576 EXPECT_TRUE(specifics
.has_header());
1577 } else if (i
% 6 == 4) {
1578 // Now we move on to NavigateAndCommit. Update the tab.
1579 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[i
].change_type());
1580 EXPECT_EQ(TabNodePool::TabIdToTag(
1581 manager()->current_machine_tag(),
1582 data
.GetSpecifics().session().tab_node_id()),
1583 syncer::SyncDataLocal(data
).GetTag());
1584 ASSERT_TRUE(specifics
.has_tab());
1585 } else if (i
% 6 == 5) {
1586 // The header needs to be updated to reflect the new window state.
1587 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[i
].change_type());
1588 ASSERT_TRUE(specifics
.has_header());
1589 header
= data
.GetSpecifics();
1593 // Verify the actual content to ensure sync sees the right data.
1594 // When it's all said and done, the header should reflect two tabs.
1595 const sync_pb::SessionHeader
& session_header
= header
.session().header();
1596 ASSERT_EQ(1, session_header
.window_size());
1597 EXPECT_EQ(2, session_header
.window(0).tab_size());
1599 // ASSERT_TRUEs above allow us to dive in freely here.
1600 // Verify first tab.
1601 const sync_pb::SessionTab
& tab1_1
=
1602 out
[2].sync_data().GetSpecifics().session().tab();
1603 ASSERT_EQ(1, tab1_1
.navigation_size());
1604 EXPECT_EQ(foo1
.spec(), tab1_1
.navigation(0).virtual_url());
1605 const sync_pb::SessionTab
& tab1_2
=
1606 out
[4].sync_data().GetSpecifics().session().tab();
1607 ASSERT_EQ(2, tab1_2
.navigation_size());
1608 EXPECT_EQ(foo1
.spec(), tab1_2
.navigation(0).virtual_url());
1609 EXPECT_EQ(foo2
.spec(), tab1_2
.navigation(1).virtual_url());
1611 // Verify second tab.
1612 const sync_pb::SessionTab
& tab2_1
=
1613 out
[8].sync_data().GetSpecifics().session().tab();
1614 ASSERT_EQ(1, tab2_1
.navigation_size());
1615 EXPECT_EQ(bar1
.spec(), tab2_1
.navigation(0).virtual_url());
1616 const sync_pb::SessionTab
& tab2_2
=
1617 out
[10].sync_data().GetSpecifics().session().tab();
1618 ASSERT_EQ(2, tab2_2
.navigation_size());
1619 EXPECT_EQ(bar1
.spec(), tab2_2
.navigation(0).virtual_url());
1620 EXPECT_EQ(bar2
.spec(), tab2_2
.navigation(1).virtual_url());
1623 // Ensure model association associates the pre-existing tabs.
1624 TEST_F(SessionsSyncManagerTest
, MergeLocalSessionExistingTabs
) {
1625 AddTab(browser(), GURL("http://foo1"));
1626 NavigateAndCommitActiveTab(GURL("http://foo2")); // Adds back entry.
1627 AddTab(browser(), GURL("http://bar1"));
1628 NavigateAndCommitActiveTab(GURL("http://bar2")); // Adds back entry.
1630 syncer::SyncChangeList out
;
1631 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
1632 ASSERT_EQ(6U, out
.size());
1634 // Check that this machine's data is not included in the foreign windows.
1635 std::vector
<const SyncedSession
*> foreign_sessions
;
1636 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions
));
1638 // Verify the header.
1639 EXPECT_TRUE(out
[0].IsValid());
1640 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[0].change_type());
1641 const SyncData
data(out
[0].sync_data());
1642 EXPECT_EQ(manager()->current_machine_tag(),
1643 syncer::SyncDataLocal(data
).GetTag());
1644 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1645 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1646 EXPECT_TRUE(specifics
.has_header());
1647 const sync_pb::SessionHeader
& header_s
= specifics
.header();
1648 EXPECT_TRUE(header_s
.has_device_type());
1649 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s
.client_name());
1650 EXPECT_EQ(0, header_s
.window_size());
1652 // Verify the tab node creations and updates with content.
1653 for (int i
= 1; i
< 5; i
++) {
1654 EXPECT_TRUE(out
[i
].IsValid());
1655 const SyncData
data(out
[i
].sync_data());
1656 EXPECT_TRUE(base::StartsWith(syncer::SyncDataLocal(data
).GetTag(),
1657 manager()->current_machine_tag(),
1658 base::CompareCase::SENSITIVE
));
1659 const sync_pb::SessionSpecifics
& specifics(data
.GetSpecifics().session());
1660 EXPECT_EQ(manager()->current_machine_tag(), specifics
.session_tag());
1662 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[i
].change_type());
1664 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[i
].change_type());
1665 EXPECT_TRUE(specifics
.has_tab());
1669 // Verify the header was updated to reflect new window state.
1670 EXPECT_TRUE(out
[5].IsValid());
1671 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[5].change_type());
1672 const SyncData
data_2(out
[5].sync_data());
1673 EXPECT_EQ(manager()->current_machine_tag(),
1674 syncer::SyncDataLocal(data_2
).GetTag());
1675 const sync_pb::SessionSpecifics
& specifics2(data_2
.GetSpecifics().session());
1676 EXPECT_EQ(manager()->current_machine_tag(), specifics2
.session_tag());
1677 EXPECT_TRUE(specifics2
.has_header());
1678 const sync_pb::SessionHeader
& header_s2
= specifics2
.header();
1679 EXPECT_EQ(1, header_s2
.window_size());
1682 SessionsSyncManager::TabLinksMap tab_map
= manager()->local_tab_map_
;
1683 ASSERT_EQ(2U, tab_map
.size());
1684 // Tabs are ordered by sessionid in tab_map, so should be able to traverse
1685 // the tree based on order of tabs created
1686 SessionsSyncManager::TabLinksMap::iterator iter
= tab_map
.begin();
1687 ASSERT_EQ(2, iter
->second
->tab()->GetEntryCount());
1688 EXPECT_EQ(GURL("http://foo1"), iter
->second
->tab()->
1689 GetEntryAtIndex(0)->GetVirtualURL());
1690 EXPECT_EQ(GURL("http://foo2"), iter
->second
->tab()->
1691 GetEntryAtIndex(1)->GetVirtualURL());
1693 ASSERT_EQ(2, iter
->second
->tab()->GetEntryCount());
1694 EXPECT_EQ(GURL("http://bar1"), iter
->second
->tab()->
1695 GetEntryAtIndex(0)->GetVirtualURL());
1696 EXPECT_EQ(GURL("http://bar2"), iter
->second
->tab()->
1697 GetEntryAtIndex(1)->GetVirtualURL());
1700 // Test garbage collection of stale foreign sessions.
1701 TEST_F(SessionsSyncManagerTest
, DoGarbageCollection
) {
1702 // Fill two instances of session specifics with a foreign session's data.
1703 std::string tag1
= "tag1";
1704 SessionID::id_type n1
[] = {5, 10, 13, 17};
1705 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
1706 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1707 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1708 tag1
, tab_list1
, &tabs1
));
1709 std::string tag2
= "tag2";
1710 SessionID::id_type n2
[] = {8, 15, 18, 20};
1711 std::vector
<SessionID::id_type
> tab_list2(n2
, n2
+ arraysize(n2
));
1712 std::vector
<sync_pb::SessionSpecifics
> tabs2
;
1713 sync_pb::SessionSpecifics
meta2(helper()->BuildForeignSession(
1714 tag2
, tab_list2
, &tabs2
));
1715 // Set the modification time for tag1 to be 21 days ago, tag2 to 5 days ago.
1716 base::Time tag1_time
= base::Time::Now() - base::TimeDelta::FromDays(21);
1717 base::Time tag2_time
= base::Time::Now() - base::TimeDelta::FromDays(5);
1719 syncer::SyncDataList foreign_data
;
1720 sync_pb::EntitySpecifics entity1
, entity2
;
1721 entity1
.mutable_session()->CopyFrom(meta
);
1722 entity2
.mutable_session()->CopyFrom(meta2
);
1723 foreign_data
.push_back(SyncData::CreateRemoteData(
1727 syncer::AttachmentIdList(),
1728 syncer::AttachmentServiceProxyForTest::Create()));
1729 foreign_data
.push_back(SyncData::CreateRemoteData(
1733 syncer::AttachmentIdList(),
1734 syncer::AttachmentServiceProxyForTest::Create()));
1735 AddTabsToSyncDataList(tabs1
, &foreign_data
);
1736 AddTabsToSyncDataList(tabs2
, &foreign_data
);
1738 syncer::SyncChangeList output
;
1739 InitWithSyncDataTakeOutput(foreign_data
, &output
);
1740 ASSERT_EQ(2U, output
.size());
1743 // Check that the foreign session was associated and retrieve the data.
1744 std::vector
<const SyncedSession
*> foreign_sessions
;
1745 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1746 ASSERT_EQ(2U, foreign_sessions
.size());
1747 foreign_sessions
.clear();
1749 // Now garbage collect and verify the non-stale session is still there.
1750 manager()->DoGarbageCollection();
1751 ASSERT_EQ(5U, output
.size());
1752 EXPECT_EQ(SyncChange::ACTION_DELETE
, output
[0].change_type());
1753 const SyncData
data(output
[0].sync_data());
1754 EXPECT_EQ(tag1
, syncer::SyncDataLocal(data
).GetTag());
1755 for (int i
= 1; i
< 5; i
++) {
1756 EXPECT_EQ(SyncChange::ACTION_DELETE
, output
[i
].change_type());
1757 const SyncData
data(output
[i
].sync_data());
1758 EXPECT_EQ(TabNodePool::TabIdToTag(tag1
, i
),
1759 syncer::SyncDataLocal(data
).GetTag());
1762 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1763 ASSERT_EQ(1U, foreign_sessions
.size());
1764 std::vector
<std::vector
<SessionID::id_type
> > session_reference
;
1765 session_reference
.push_back(tab_list2
);
1766 helper()->VerifySyncedSession(tag2
, session_reference
,
1767 *(foreign_sessions
[0]));
1770 // Test that an update to a previously considered "stale" session,
1771 // prior to garbage collection, will save the session from deletion.
1772 TEST_F(SessionsSyncManagerTest
, GarbageCollectionHonoursUpdate
) {
1773 std::string tag1
= "tag1";
1774 SessionID::id_type n1
[] = {5, 10, 13, 17};
1775 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
1776 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1777 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1778 tag1
, tab_list1
, &tabs1
));
1779 syncer::SyncDataList foreign_data
;
1780 sync_pb::EntitySpecifics entity1
;
1781 base::Time tag1_time
= base::Time::Now() - base::TimeDelta::FromDays(21);
1782 entity1
.mutable_session()->CopyFrom(meta
);
1783 foreign_data
.push_back(SyncData::CreateRemoteData(
1787 syncer::AttachmentIdList(),
1788 syncer::AttachmentServiceProxyForTest::Create()));
1789 AddTabsToSyncDataList(tabs1
, &foreign_data
);
1790 syncer::SyncChangeList output
;
1791 InitWithSyncDataTakeOutput(foreign_data
, &output
);
1792 ASSERT_EQ(2U, output
.size());
1794 // Update to a non-stale time.
1795 sync_pb::EntitySpecifics update_entity
;
1796 update_entity
.mutable_session()->CopyFrom(tabs1
[0]);
1797 syncer::SyncChangeList changes
;
1799 syncer::SyncChange(FROM_HERE
,
1800 SyncChange::ACTION_UPDATE
,
1801 syncer::SyncData::CreateRemoteData(
1805 syncer::AttachmentIdList(),
1806 syncer::AttachmentServiceProxyForTest::Create())));
1807 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1809 // Check that the foreign session was associated and retrieve the data.
1810 std::vector
<const SyncedSession
*> foreign_sessions
;
1811 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1812 ASSERT_EQ(1U, foreign_sessions
.size());
1813 foreign_sessions
.clear();
1815 // Verify the now non-stale session does not get deleted.
1816 manager()->DoGarbageCollection();
1817 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
1818 ASSERT_EQ(1U, foreign_sessions
.size());
1819 std::vector
<std::vector
<SessionID::id_type
> > session_reference
;
1820 session_reference
.push_back(tab_list1
);
1821 helper()->VerifySyncedSession(
1822 tag1
, session_reference
, *(foreign_sessions
[0]));
1825 // Test that swapping WebContents for a tab is properly observed and handled
1826 // by the SessionsSyncManager.
1827 TEST_F(SessionsSyncManagerTest
, CheckPrerenderedWebContentsSwap
) {
1828 AddTab(browser(), GURL("http://foo1"));
1829 NavigateAndCommitActiveTab(GURL("http://foo2"));
1831 syncer::SyncChangeList out
;
1832 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out
);
1833 ASSERT_EQ(4U, out
.size()); // Header, tab ADD, tab UPDATE, header UPDATE.
1835 // To simulate WebContents swap during prerendering, create new WebContents
1836 // and swap with old WebContents.
1837 scoped_ptr
<content::WebContents
> old_web_contents
;
1838 old_web_contents
.reset(browser()->tab_strip_model()->GetActiveWebContents());
1840 // Create new WebContents, with the required tab helpers.
1841 WebContents
* new_web_contents
= WebContents::CreateWithSessionStorage(
1842 WebContents::CreateParams(profile()),
1843 old_web_contents
->GetController().GetSessionStorageNamespaceMap());
1844 SessionTabHelper::CreateForWebContents(new_web_contents
);
1845 TabContentsSyncedTabDelegate::CreateForWebContents(new_web_contents
);
1846 new_web_contents
->GetController()
1847 .CopyStateFrom(old_web_contents
->GetController());
1849 // Swap the WebContents.
1850 int index
= browser()->tab_strip_model()->GetIndexOfWebContents(
1851 old_web_contents
.get());
1852 browser()->tab_strip_model()->ReplaceWebContentsAt(index
, new_web_contents
);
1854 ASSERT_EQ(9U, out
.size());
1855 EXPECT_EQ(SyncChange::ACTION_ADD
, out
[4].change_type());
1856 EXPECT_EQ(SyncChange::ACTION_UPDATE
, out
[5].change_type());
1859 NavigateAndCommitActiveTab(GURL("http://bar2"));
1861 // Delete old WebContents. This should not crash.
1862 old_web_contents
.reset();
1864 // Try more navigations and verify output size. This can also reveal
1865 // bugs (leaks) on memcheck bots if the SessionSyncManager
1866 // didn't properly clean up the tab pool or session tracker.
1867 NavigateAndCommitActiveTab(GURL("http://bar3"));
1869 AddTab(browser(), GURL("http://bar4"));
1870 NavigateAndCommitActiveTab(GURL("http://bar5"));
1871 ASSERT_EQ(19U, out
.size());
1875 class SessionNotificationObserver
: public content::NotificationObserver
{
1877 SessionNotificationObserver() : notified_of_update_(false),
1878 notified_of_refresh_(false) {
1879 registrar_
.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED
,
1880 content::NotificationService::AllSources());
1881 registrar_
.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL
,
1882 content::NotificationService::AllSources());
1884 void Observe(int type
,
1885 const content::NotificationSource
& source
,
1886 const content::NotificationDetails
& details
) override
{
1888 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED
:
1889 notified_of_update_
= true;
1891 case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL
:
1892 notified_of_refresh_
= true;
1899 bool notified_of_update() const { return notified_of_update_
; }
1900 bool notified_of_refresh() const { return notified_of_refresh_
; }
1902 notified_of_update_
= false;
1903 notified_of_refresh_
= false;
1906 content::NotificationRegistrar registrar_
;
1907 bool notified_of_update_
;
1908 bool notified_of_refresh_
;
1912 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when processing
1914 TEST_F(SessionsSyncManagerTest
, NotifiedOfUpdates
) {
1915 SessionNotificationObserver observer
;
1916 ASSERT_FALSE(observer
.notified_of_update());
1917 InitWithNoSyncData();
1919 SessionID::id_type n
[] = {5};
1920 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1921 std::vector
<SessionID::id_type
> tab_list(n
, n
+ arraysize(n
));
1922 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1923 "tag1", tab_list
, &tabs1
));
1925 syncer::SyncChangeList changes
;
1926 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_ADD
));
1927 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1928 EXPECT_TRUE(observer
.notified_of_update());
1932 AddTabsToChangeList(tabs1
, SyncChange::ACTION_ADD
, &changes
);
1933 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1934 EXPECT_TRUE(observer
.notified_of_update());
1938 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_DELETE
));
1939 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1940 EXPECT_TRUE(observer
.notified_of_update());
1943 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when handling
1944 // local hide/removal of foreign session.
1945 TEST_F(SessionsSyncManagerTest
, NotifiedOfLocalRemovalOfForeignSession
) {
1946 InitWithNoSyncData();
1947 const std::string
tag("tag1");
1948 SessionID::id_type n
[] = {5};
1949 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1950 std::vector
<SessionID::id_type
> tab_list(n
, n
+ arraysize(n
));
1951 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
1952 tag
, tab_list
, &tabs1
));
1954 syncer::SyncChangeList changes
;
1955 changes
.push_back(MakeRemoteChange(1, meta
, SyncChange::ACTION_ADD
));
1956 manager()->ProcessSyncChanges(FROM_HERE
, changes
);
1958 SessionNotificationObserver observer
;
1959 ASSERT_FALSE(observer
.notified_of_update());
1960 manager()->DeleteForeignSession(tag
);
1961 ASSERT_TRUE(observer
.notified_of_update());
1964 #if defined(OS_ANDROID) || defined(OS_IOS)
1965 // Tests that opening the other devices page triggers a session sync refresh.
1966 // This page only exists on mobile platforms today; desktop has a
1967 // search-enhanced NTP without other devices.
1968 TEST_F(SessionsSyncManagerTest
, NotifiedOfRefresh
) {
1969 SessionNotificationObserver observer
;
1970 ASSERT_FALSE(observer
.notified_of_refresh());
1971 InitWithNoSyncData();
1972 AddTab(browser(), GURL("http://foo1"));
1973 EXPECT_FALSE(observer
.notified_of_refresh());
1974 NavigateAndCommitActiveTab(GURL("chrome://newtab/#open_tabs"));
1975 EXPECT_TRUE(observer
.notified_of_refresh());
1977 #endif // defined(OS_ANDROID) || defined(OS_IOS)
1979 // Tests receipt of duplicate tab IDs in the same window. This should never
1980 // happen, but we want to make sure the client won't do anything bad if it does
1981 // receive such garbage input data.
1982 TEST_F(SessionsSyncManagerTest
, ReceiveDuplicateTabInSameWindow
) {
1983 std::string tag
= "tag1";
1985 // Reuse tab ID 10 in an attempt to trigger bad behavior.
1986 SessionID::id_type n1
[] = {5, 10, 10, 17};
1987 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
1988 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
1989 sync_pb::SessionSpecifics
meta(
1990 helper()->BuildForeignSession(tag
, tab_list1
, &tabs1
));
1992 // Set up initial data.
1993 syncer::SyncDataList initial_data
;
1994 sync_pb::EntitySpecifics entity
;
1995 entity
.mutable_session()->CopyFrom(meta
);
1996 initial_data
.push_back(SyncData::CreateRemoteData(
2000 syncer::AttachmentIdList(),
2001 syncer::AttachmentServiceProxyForTest::Create()));
2002 AddTabsToSyncDataList(tabs1
, &initial_data
);
2004 syncer::SyncChangeList output
;
2005 InitWithSyncDataTakeOutput(initial_data
, &output
);
2008 // Tests receipt of duplicate tab IDs for the same session. The duplicate tab
2009 // ID is present in two different windows. A client can't be expected to do
2010 // anything reasonable with this input, but we can expect that it doesn't
2012 TEST_F(SessionsSyncManagerTest
, ReceiveDuplicateTabInOtherWindow
) {
2013 std::string tag
= "tag1";
2015 SessionID::id_type n1
[] = {5, 10, 17};
2016 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
2017 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
2018 sync_pb::SessionSpecifics
meta(
2019 helper()->BuildForeignSession(tag
, tab_list1
, &tabs1
));
2021 // Add a second window. Tab ID 10 is a duplicate.
2022 SessionID::id_type n2
[] = {10, 18, 20};
2023 std::vector
<SessionID::id_type
> tab_list2(n2
, n2
+ arraysize(n2
));
2024 helper()->AddWindowSpecifics(1, tab_list2
, &meta
);
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()));
2036 AddTabsToSyncDataList(tabs1
, &initial_data
);
2038 for (size_t i
= 0; i
< tab_list2
.size(); ++i
) {
2039 sync_pb::EntitySpecifics entity
;
2040 helper()->BuildTabSpecifics(tag
, 0, tab_list2
[i
], entity
.mutable_session());
2041 initial_data
.push_back(SyncData::CreateRemoteData(
2045 syncer::AttachmentIdList(),
2046 syncer::AttachmentServiceProxyForTest::Create()));
2049 syncer::SyncChangeList output
;
2050 InitWithSyncDataTakeOutput(initial_data
, &output
);
2053 // Tests receipt of multiple unassociated tabs and makes sure that
2054 // the ones with later timestamp win
2055 TEST_F(SessionsSyncManagerTest
, ReceiveDuplicateUnassociatedTabs
) {
2056 std::string tag
= "tag1";
2058 SessionID::id_type n1
[] = {5, 10, 17};
2059 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
2060 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
2061 sync_pb::SessionSpecifics
meta(
2062 helper()->BuildForeignSession(tag
, tab_list1
, &tabs1
));
2064 // Set up initial data.
2065 syncer::SyncDataList initial_data
;
2066 sync_pb::EntitySpecifics entity
;
2067 entity
.mutable_session()->CopyFrom(meta
);
2068 initial_data
.push_back(SyncData::CreateRemoteData(
2072 syncer::AttachmentIdList(),
2073 syncer::AttachmentServiceProxyForTest::Create()));
2077 for (size_t i
= 0; i
< tabs1
.size(); i
++) {
2078 entity
.mutable_session()->CopyFrom(tabs1
[i
]);
2079 initial_data
.push_back(SyncData::CreateRemoteData(
2082 base::Time::FromDoubleT(2000),
2083 syncer::AttachmentIdList(),
2084 syncer::AttachmentServiceProxyForTest::Create()));
2087 // Add two more tabs with duplicating IDs but with different modification
2088 // times, one before and one after the tabs above.
2089 // These two tabs get a different visual indices to distinguish them from the
2090 // tabs above that get visual index 1 by default.
2091 sync_pb::SessionSpecifics duplicating_tab1
;
2092 helper()->BuildTabSpecifics(tag
, 0, 10, &duplicating_tab1
);
2093 duplicating_tab1
.mutable_tab()->set_tab_visual_index(2);
2094 entity
.mutable_session()->CopyFrom(duplicating_tab1
);
2095 initial_data
.push_back(SyncData::CreateRemoteData(
2098 base::Time::FromDoubleT(1000),
2099 syncer::AttachmentIdList(),
2100 syncer::AttachmentServiceProxyForTest::Create()));
2102 sync_pb::SessionSpecifics duplicating_tab2
;
2103 helper()->BuildTabSpecifics(tag
, 0, 17, &duplicating_tab2
);
2104 duplicating_tab2
.mutable_tab()->set_tab_visual_index(3);
2105 entity
.mutable_session()->CopyFrom(duplicating_tab2
);
2106 initial_data
.push_back(SyncData::CreateRemoteData(
2109 base::Time::FromDoubleT(3000),
2110 syncer::AttachmentIdList(),
2111 syncer::AttachmentServiceProxyForTest::Create()));
2113 syncer::SyncChangeList output
;
2114 InitWithSyncDataTakeOutput(initial_data
, &output
);
2116 std::vector
<const SyncedSession
*> foreign_sessions
;
2117 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
2119 const std::vector
<sessions::SessionTab
*>& window_tabs
=
2120 foreign_sessions
[0]->windows
.find(0)->second
->tabs
;
2121 ASSERT_EQ(3U, window_tabs
.size());
2122 // The first one is from the original set of tabs.
2123 ASSERT_EQ(1, window_tabs
[0]->tab_visual_index
);
2124 // The one from the original set of tabs wins over duplicating_tab1.
2125 ASSERT_EQ(1, window_tabs
[1]->tab_visual_index
);
2126 // duplicating_tab2 wins due to the later timestamp.
2127 ASSERT_EQ(3, window_tabs
[2]->tab_visual_index
);
2130 // Verify that GetAllForeignSessions returns all sessions sorted by recency.
2131 TEST_F(SessionsSyncManagerTest
, GetAllForeignSessions
) {
2132 SessionID::id_type ids
[] = {5, 10, 13, 17};
2133 std::vector
<SessionID::id_type
> tab_list(ids
, ids
+ arraysize(ids
));
2135 const std::string kTag
= "tag1";
2136 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
2137 sync_pb::SessionSpecifics
meta1(helper()->BuildForeignSession(
2138 kTag
, tab_list
, &tabs1
));
2140 const std::string kTag2
= "tag2";
2141 std::vector
<sync_pb::SessionSpecifics
> tabs2
;
2142 sync_pb::SessionSpecifics
meta2(helper()->BuildForeignSession(
2143 kTag2
, tab_list
, &tabs2
));
2145 sync_pb::EntitySpecifics entity1
;
2146 entity1
.mutable_session()->CopyFrom(meta1
);
2147 sync_pb::EntitySpecifics entity2
;
2148 entity2
.mutable_session()->CopyFrom(meta2
);
2150 syncer::SyncDataList initial_data
;
2151 initial_data
.push_back(SyncData::CreateRemoteData(
2154 base::Time::FromInternalValue(10),
2155 syncer::AttachmentIdList(),
2156 syncer::AttachmentServiceProxyForTest::Create()));
2157 AddTabsToSyncDataList(tabs1
, &initial_data
);
2158 initial_data
.push_back(SyncData::CreateRemoteData(
2161 base::Time::FromInternalValue(200),
2162 syncer::AttachmentIdList(),
2163 syncer::AttachmentServiceProxyForTest::Create()));
2164 AddTabsToSyncDataList(tabs2
, &initial_data
);
2166 syncer::SyncChangeList output
;
2167 InitWithSyncDataTakeOutput(initial_data
, &output
);
2169 std::vector
<const SyncedSession
*> foreign_sessions
;
2170 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions
));
2171 ASSERT_EQ(2U, foreign_sessions
.size());
2172 ASSERT_GT(foreign_sessions
[0]->modified_time
,
2173 foreign_sessions
[1]->modified_time
);
2176 // Verify that GetForeignSessionTabs returns all tabs for a session sorted
2178 TEST_F(SessionsSyncManagerTest
, GetForeignSessionTabs
) {
2179 const std::string kTag
= "tag1";
2181 SessionID::id_type n1
[] = {5, 10, 13, 17};
2182 std::vector
<SessionID::id_type
> tab_list1(n1
, n1
+ arraysize(n1
));
2183 std::vector
<sync_pb::SessionSpecifics
> tabs1
;
2184 sync_pb::SessionSpecifics
meta(helper()->BuildForeignSession(
2185 kTag
, tab_list1
, &tabs1
));
2186 // Add a second window.
2187 SessionID::id_type n2
[] = {7, 15, 18, 20};
2188 std::vector
<SessionID::id_type
> tab_list2(n2
, n2
+ arraysize(n2
));
2189 helper()->AddWindowSpecifics(1, tab_list2
, &meta
);
2191 // Set up initial data.
2192 syncer::SyncDataList initial_data
;
2193 sync_pb::EntitySpecifics entity
;
2194 entity
.mutable_session()->CopyFrom(meta
);
2195 initial_data
.push_back(SyncData::CreateRemoteData(
2199 syncer::AttachmentIdList(),
2200 syncer::AttachmentServiceProxyForTest::Create()));
2202 // Add the first window's tabs.
2203 AddTabsToSyncDataList(tabs1
, &initial_data
);
2205 // Add the second window's tabs.
2206 for (size_t i
= 0; i
< tab_list2
.size(); ++i
) {
2207 sync_pb::EntitySpecifics entity
;
2208 helper()->BuildTabSpecifics(kTag
, 0, tab_list2
[i
],
2209 entity
.mutable_session());
2210 // Order the tabs oldest to most ReceiveDuplicateUnassociatedTabs and
2211 // left to right visually.
2212 initial_data
.push_back(SyncData::CreateRemoteData(
2215 base::Time::FromInternalValue(i
+ 1),
2216 syncer::AttachmentIdList(),
2217 syncer::AttachmentServiceProxyForTest::Create()));
2220 syncer::SyncChangeList output
;
2221 InitWithSyncDataTakeOutput(initial_data
, &output
);
2223 std::vector
<const sessions::SessionTab
*> tabs
;
2224 ASSERT_TRUE(manager()->GetForeignSessionTabs(kTag
, &tabs
));
2225 // Assert that the size matches the total number of tabs and that the order
2226 // is from most recent to least.
2227 ASSERT_EQ(tab_list1
.size() + tab_list2
.size(), tabs
.size());
2228 base::Time last_time
;
2229 for (size_t i
= 0; i
< tabs
.size(); ++i
) {
2230 base::Time this_time
= tabs
[i
]->timestamp
;
2232 ASSERT_GE(last_time
, this_time
);
2233 last_time
= tabs
[i
]->timestamp
;
2237 } // namespace browser_sync