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