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.
4
5 #include "chrome/browser/sync/sessions/sessions_sync_manager.h"
6
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/sessions/session_types.h"
11 #include "chrome/browser/sync/glue/local_device_info_provider_mock.h"
12 #include "chrome/browser/sync/glue/session_sync_test_helper.h"
13 #include "chrome/browser/sync/glue/synced_tab_delegate.h"
14 #include "chrome/browser/sync/glue/synced_window_delegate.h"
15 #include "chrome/browser/sync/sessions/notification_service_sessions_router.h"
16 #include "chrome/browser/sync/sessions/sessions_util.h"
17 #include "chrome/browser/sync/sessions/synced_window_delegates_getter.h"
18 #include "chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chrome/test/base/browser_with_test_window_test.h"
21 #include "components/sessions/serialized_navigation_entry_test_helper.h"
22 #include "components/sessions/session_id.h"
23 #include "components/sync_driver/device_info.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"
34
35 using content::WebContents;
36 using sessions::SerializedNavigationEntry;
37 using sessions::SerializedNavigationEntryTestHelper;
38 using sync_driver::DeviceInfo;
39 using sync_driver::LocalDeviceInfoProvider;
40 using syncer::SyncChange;
41 using syncer::SyncData;
42
43 namespace browser_sync {
44
45 namespace {
46
47 class SyncedWindowDelegateOverride : public SyncedWindowDelegate {
48 public:
SyncedWindowDelegateOverride(SyncedWindowDelegate * wrapped)49 explicit SyncedWindowDelegateOverride(SyncedWindowDelegate* wrapped)
50 : wrapped_(wrapped) {
51 }
~SyncedWindowDelegateOverride()52 virtual ~SyncedWindowDelegateOverride() {}
53
HasWindow() const54 virtual bool HasWindow() const OVERRIDE {
55 return wrapped_->HasWindow();
56 }
57
GetSessionId() const58 virtual SessionID::id_type GetSessionId() const OVERRIDE {
59 return wrapped_->GetSessionId();
60 }
61
GetTabCount() const62 virtual int GetTabCount() const OVERRIDE {
63 return wrapped_->GetTabCount();
64 }
65
GetActiveIndex() const66 virtual int GetActiveIndex() const OVERRIDE {
67 return wrapped_->GetActiveIndex();
68 }
69
IsApp() const70 virtual bool IsApp() const OVERRIDE {
71 return wrapped_->IsApp();
72 }
73
IsTypeTabbed() const74 virtual bool IsTypeTabbed() const OVERRIDE {
75 return wrapped_->IsTypeTabbed();
76 }
77
IsTypePopup() const78 virtual bool IsTypePopup() const OVERRIDE {
79 return wrapped_->IsTypePopup();
80 }
81
IsTabPinned(const SyncedTabDelegate * tab) const82 virtual bool IsTabPinned(const SyncedTabDelegate* tab) const OVERRIDE {
83 return wrapped_->IsTabPinned(tab);
84 }
85
GetTabAt(int index) const86 virtual SyncedTabDelegate* GetTabAt(int index) const OVERRIDE {
87 if (tab_overrides_.find(index) != tab_overrides_.end())
88 return tab_overrides_.find(index)->second;
89
90 return wrapped_->GetTabAt(index);
91 }
92
OverrideTabAt(int index,SyncedTabDelegate * delegate,SessionID::id_type tab_id)93 void OverrideTabAt(int index,
94 SyncedTabDelegate* delegate,
95 SessionID::id_type tab_id) {
96 tab_overrides_[index] = delegate;
97 tab_id_overrides_[index] = tab_id;
98 }
99
GetTabIdAt(int index) const100 virtual SessionID::id_type GetTabIdAt(int index) const OVERRIDE {
101 if (tab_id_overrides_.find(index) != tab_id_overrides_.end())
102 return tab_id_overrides_.find(index)->second;
103 return wrapped_->GetTabIdAt(index);
104 }
105
IsSessionRestoreInProgress() const106 virtual bool IsSessionRestoreInProgress() const OVERRIDE {
107 return wrapped_->IsSessionRestoreInProgress();
108 }
109
110 private:
111 std::map<int, SyncedTabDelegate*> tab_overrides_;
112 std::map<int, SessionID::id_type> tab_id_overrides_;
113 SyncedWindowDelegate* wrapped_;
114 };
115
116 class TestSyncedWindowDelegatesGetter : public SyncedWindowDelegatesGetter {
117 public:
TestSyncedWindowDelegatesGetter(const std::set<SyncedWindowDelegate * > & delegates)118 TestSyncedWindowDelegatesGetter(
119 const std::set<SyncedWindowDelegate*>& delegates)
120 : delegates_(delegates) {}
121
GetSyncedWindowDelegates()122 virtual const std::set<SyncedWindowDelegate*> GetSyncedWindowDelegates()
123 OVERRIDE {
124 return delegates_;
125 }
126 private:
127 const std::set<SyncedWindowDelegate*> delegates_;
128 };
129
130 class TestSyncProcessorStub : public syncer::SyncChangeProcessor {
131 public:
TestSyncProcessorStub(syncer::SyncChangeList * output)132 explicit TestSyncProcessorStub(syncer::SyncChangeList* output)
133 : output_(output) {}
ProcessSyncChanges(const tracked_objects::Location & from_here,const syncer::SyncChangeList & change_list)134 virtual syncer::SyncError ProcessSyncChanges(
135 const tracked_objects::Location& from_here,
136 const syncer::SyncChangeList& change_list) OVERRIDE {
137 if (error_.IsSet()) {
138 syncer::SyncError error = error_;
139 error_ = syncer::SyncError();
140 return error;
141 }
142
143 if (output_)
144 output_->insert(output_->end(), change_list.begin(), change_list.end());
145
146 return syncer::SyncError();
147 }
148
GetAllSyncData(syncer::ModelType type) const149 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type)
150 const OVERRIDE {
151 return sync_data_to_return_;
152 }
153
FailProcessSyncChangesWith(const syncer::SyncError & error)154 void FailProcessSyncChangesWith(const syncer::SyncError& error) {
155 error_ = error;
156 }
157
SetSyncDataToReturn(const syncer::SyncDataList & data)158 void SetSyncDataToReturn(const syncer::SyncDataList& data) {
159 sync_data_to_return_ = data;
160 }
161
162 private:
163 syncer::SyncError error_;
164 syncer::SyncChangeList* output_;
165 syncer::SyncDataList sync_data_to_return_;
166 };
167
MakeRemoteChange(int64 id,const sync_pb::SessionSpecifics & specifics,SyncChange::SyncChangeType type)168 syncer::SyncChange MakeRemoteChange(
169 int64 id,
170 const sync_pb::SessionSpecifics& specifics,
171 SyncChange::SyncChangeType type) {
172 sync_pb::EntitySpecifics entity;
173 entity.mutable_session()->CopyFrom(specifics);
174 return syncer::SyncChange(
175 FROM_HERE,
176 type,
177 syncer::SyncData::CreateRemoteData(
178 id,
179 entity,
180 base::Time(),
181 syncer::AttachmentIdList(),
182 syncer::AttachmentServiceProxyForTest::Create()));
183 }
184
AddTabsToChangeList(const std::vector<sync_pb::SessionSpecifics> & batch,SyncChange::SyncChangeType type,syncer::SyncChangeList * change_list)185 void AddTabsToChangeList(
186 const std::vector<sync_pb::SessionSpecifics>& batch,
187 SyncChange::SyncChangeType type,
188 syncer::SyncChangeList* change_list) {
189 std::vector<sync_pb::SessionSpecifics>::const_iterator iter;
190 for (iter = batch.begin();
191 iter != batch.end(); ++iter) {
192 sync_pb::EntitySpecifics entity;
193 entity.mutable_session()->CopyFrom(*iter);
194 change_list->push_back(syncer::SyncChange(
195 FROM_HERE,
196 type,
197 syncer::SyncData::CreateRemoteData(
198 iter->tab_node_id(),
199 entity,
200 base::Time(),
201 syncer::AttachmentIdList(),
202 syncer::AttachmentServiceProxyForTest::Create())));
203 }
204 }
205
AddTabsToSyncDataList(const std::vector<sync_pb::SessionSpecifics> tabs,syncer::SyncDataList * list)206 void AddTabsToSyncDataList(const std::vector<sync_pb::SessionSpecifics> tabs,
207 syncer::SyncDataList* list) {
208 for (size_t i = 0; i < tabs.size(); i++) {
209 sync_pb::EntitySpecifics entity;
210 entity.mutable_session()->CopyFrom(tabs[i]);
211 list->push_back(SyncData::CreateRemoteData(
212 i + 2,
213 entity,
214 base::Time(),
215 syncer::AttachmentIdList(),
216 syncer::AttachmentServiceProxyForTest::Create()));
217 }
218 }
219
220 class DummyRouter : public LocalSessionEventRouter {
221 public:
~DummyRouter()222 virtual ~DummyRouter() {}
StartRoutingTo(LocalSessionEventHandler * handler)223 virtual void StartRoutingTo(LocalSessionEventHandler* handler) OVERRIDE {}
Stop()224 virtual void Stop() OVERRIDE {}
225 };
226
NewDummyRouter()227 scoped_ptr<LocalSessionEventRouter> NewDummyRouter() {
228 return scoped_ptr<LocalSessionEventRouter>(new DummyRouter());
229 }
230
231 } // namespace
232
233 class SessionsSyncManagerTest
234 : public BrowserWithTestWindowTest {
235 public:
SessionsSyncManagerTest()236 SessionsSyncManagerTest()
237 : test_processor_(NULL) {
238 local_device_.reset(new LocalDeviceInfoProviderMock(
239 "cache_guid",
240 "Wayne Gretzky's Hacking Box",
241 "Chromium 10k",
242 "Chrome 10k",
243 sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
244 "device_id"));
245 }
246
SetUp()247 virtual void SetUp() OVERRIDE {
248 BrowserWithTestWindowTest::SetUp();
249 browser_sync::NotificationServiceSessionsRouter* router(
250 new browser_sync::NotificationServiceSessionsRouter(
251 profile(), syncer::SyncableService::StartSyncFlare()));
252 manager_.reset(new SessionsSyncManager(profile(), local_device_.get(),
253 scoped_ptr<LocalSessionEventRouter>(router)));
254 }
255
TearDown()256 virtual void TearDown() OVERRIDE {
257 test_processor_ = NULL;
258 helper()->Reset();
259 manager_.reset();
260 BrowserWithTestWindowTest::TearDown();
261 }
262
GetLocalDeviceInfo()263 const DeviceInfo* GetLocalDeviceInfo() {
264 return local_device_->GetLocalDeviceInfo();
265 }
266
manager()267 SessionsSyncManager* manager() { return manager_.get(); }
helper()268 SessionSyncTestHelper* helper() { return &helper_; }
local_device()269 LocalDeviceInfoProvider* local_device() { return local_device_.get(); }
270
InitWithSyncDataTakeOutput(const syncer::SyncDataList & initial_data,syncer::SyncChangeList * output)271 void InitWithSyncDataTakeOutput(const syncer::SyncDataList& initial_data,
272 syncer::SyncChangeList* output) {
273 test_processor_ = new TestSyncProcessorStub(output);
274 syncer::SyncMergeResult result = manager_->MergeDataAndStartSyncing(
275 syncer::SESSIONS, initial_data,
276 scoped_ptr<syncer::SyncChangeProcessor>(test_processor_),
277 scoped_ptr<syncer::SyncErrorFactory>(
278 new syncer::SyncErrorFactoryMock()));
279 EXPECT_FALSE(result.error().IsSet());
280 }
281
InitWithNoSyncData()282 void InitWithNoSyncData() {
283 InitWithSyncDataTakeOutput(syncer::SyncDataList(), NULL);
284 }
285
TriggerProcessSyncChangesError()286 void TriggerProcessSyncChangesError() {
287 test_processor_->FailProcessSyncChangesWith(syncer::SyncError(
288 FROM_HERE, syncer::SyncError::DATATYPE_ERROR, "Error",
289 syncer::SESSIONS));
290 }
291
SetSyncData(const syncer::SyncDataList & data)292 void SetSyncData(const syncer::SyncDataList& data) {
293 test_processor_->SetSyncDataToReturn(data);
294 }
295
FilterOutLocalHeaderChanges(syncer::SyncChangeList * list)296 syncer::SyncChangeList* FilterOutLocalHeaderChanges(
297 syncer::SyncChangeList* list) {
298 syncer::SyncChangeList::iterator it = list->begin();
299 bool found = false;
300 while (it != list->end()) {
301 if (syncer::SyncDataLocal(it->sync_data()).GetTag() ==
302 manager_->current_machine_tag()) {
303 EXPECT_TRUE(SyncChange::ACTION_ADD == it->change_type() ||
304 SyncChange::ACTION_UPDATE == it->change_type());
305 it = list->erase(it);
306 found = true;
307 } else {
308 ++it;
309 }
310 }
311 EXPECT_TRUE(found);
312 return list;
313 }
314
315 private:
316 scoped_ptr<SessionsSyncManager> manager_;
317 SessionSyncTestHelper helper_;
318 TestSyncProcessorStub* test_processor_;
319 scoped_ptr<LocalDeviceInfoProviderMock> local_device_;
320 };
321
322 // Test that the SyncSessionManager can properly fill in a SessionHeader.
TEST_F(SessionsSyncManagerTest,PopulateSessionHeader)323 TEST_F(SessionsSyncManagerTest, PopulateSessionHeader) {
324 sync_pb::SessionHeader header_s;
325 header_s.set_client_name("Client 1");
326 header_s.set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_WIN);
327
328 SyncedSession session;
329 base::Time time = base::Time::Now();
330 SessionsSyncManager::PopulateSessionHeaderFromSpecifics(
331 header_s, time, &session);
332 ASSERT_EQ("Client 1", session.session_name);
333 ASSERT_EQ(SyncedSession::TYPE_WIN, session.device_type);
334 ASSERT_EQ(time, session.modified_time);
335 }
336
337 // Test translation between protobuf types and chrome session types.
TEST_F(SessionsSyncManagerTest,PopulateSessionWindow)338 TEST_F(SessionsSyncManagerTest, PopulateSessionWindow) {
339 sync_pb::SessionWindow window_s;
340 window_s.add_tab(0);
341 window_s.set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
342 window_s.set_selected_tab_index(1);
343
344 std::string tag = "tag";
345 SyncedSession* session = manager()->session_tracker_.GetSession(tag);
346 manager()->session_tracker_.PutWindowInSession(tag, 0);
347 manager()->BuildSyncedSessionFromSpecifics(
348 tag, window_s, base::Time(), session->windows[0]);
349 ASSERT_EQ(1U, session->windows[0]->tabs.size());
350 ASSERT_EQ(1, session->windows[0]->selected_tab_index);
351 ASSERT_EQ(1, session->windows[0]->type);
352 ASSERT_EQ(1U, manager()->session_tracker_.num_synced_sessions());
353 ASSERT_EQ(1U,
354 manager()->session_tracker_.num_synced_tabs(std::string("tag")));
355 }
356
357 namespace {
358
359 class SyncedTabDelegateFake : public SyncedTabDelegate {
360 public:
SyncedTabDelegateFake()361 SyncedTabDelegateFake() : current_entry_index_(0),
362 pending_entry_index_(-1),
363 is_supervised_(false),
364 sync_id_(-1),
365 blocked_navigations_(NULL) {}
~SyncedTabDelegateFake()366 virtual ~SyncedTabDelegateFake() {}
367
GetCurrentEntryIndex() const368 virtual int GetCurrentEntryIndex() const OVERRIDE {
369 return current_entry_index_;
370 }
set_current_entry_index(int i)371 void set_current_entry_index(int i) {
372 current_entry_index_ = i;
373 }
374
GetEntryAtIndex(int i) const375 virtual content::NavigationEntry* GetEntryAtIndex(int i) const OVERRIDE {
376 const int size = entries_.size();
377 return (size < i + 1) ? NULL : entries_[i];
378 }
379
AppendEntry(content::NavigationEntry * entry)380 void AppendEntry(content::NavigationEntry* entry) {
381 entries_.push_back(entry);
382 }
383
GetEntryCount() const384 virtual int GetEntryCount() const OVERRIDE {
385 return entries_.size();
386 }
387
GetPendingEntryIndex() const388 virtual int GetPendingEntryIndex() const OVERRIDE {
389 return pending_entry_index_;
390 }
set_pending_entry_index(int i)391 void set_pending_entry_index(int i) {
392 pending_entry_index_ = i;
393 }
394
GetWindowId() const395 virtual SessionID::id_type GetWindowId() const OVERRIDE {
396 return SessionID::id_type();
397 }
398
GetSessionId() const399 virtual SessionID::id_type GetSessionId() const OVERRIDE {
400 return SessionID::id_type();
401 }
402
IsBeingDestroyed() const403 virtual bool IsBeingDestroyed() const OVERRIDE { return false; }
profile() const404 virtual Profile* profile() const OVERRIDE { return NULL; }
GetExtensionAppId() const405 virtual std::string GetExtensionAppId() const OVERRIDE {
406 return std::string();
407 }
GetPendingEntry() const408 virtual content::NavigationEntry* GetPendingEntry() const OVERRIDE {
409 return NULL;
410 }
GetActiveEntry() const411 virtual content::NavigationEntry* GetActiveEntry() const OVERRIDE {
412 return NULL;
413 }
ProfileIsSupervised() const414 virtual bool ProfileIsSupervised() const OVERRIDE {
415 return is_supervised_;
416 }
set_is_supervised(bool is_supervised)417 void set_is_supervised(bool is_supervised) { is_supervised_ = is_supervised; }
418 virtual const std::vector<const content::NavigationEntry*>*
GetBlockedNavigations() const419 GetBlockedNavigations() const OVERRIDE {
420 return blocked_navigations_;
421 }
set_blocked_navigations(std::vector<const content::NavigationEntry * > * navs)422 void set_blocked_navigations(
423 std::vector<const content::NavigationEntry*>* navs) {
424 blocked_navigations_ = navs;
425 }
IsPinned() const426 virtual bool IsPinned() const OVERRIDE {
427 return false;
428 }
HasWebContents() const429 virtual bool HasWebContents() const OVERRIDE {
430 return false;
431 }
GetWebContents() const432 virtual content::WebContents* GetWebContents() const OVERRIDE {
433 return NULL;
434 }
435
436 // Session sync related methods.
GetSyncId() const437 virtual int GetSyncId() const OVERRIDE {
438 return sync_id_;
439 }
SetSyncId(int sync_id)440 virtual void SetSyncId(int sync_id) OVERRIDE {
441 sync_id_ = sync_id;
442 }
443
reset()444 void reset() {
445 current_entry_index_ = 0;
446 pending_entry_index_ = -1;
447 sync_id_ = -1;
448 entries_.clear();
449 }
450
451 private:
452 int current_entry_index_;
453 int pending_entry_index_;
454 bool is_supervised_;
455 int sync_id_;
456 std::vector<const content::NavigationEntry*>* blocked_navigations_;
457 ScopedVector<content::NavigationEntry> entries_;
458 };
459
460 } // namespace
461
462 // Test that we exclude tabs with only chrome:// and file:// schemed navigations
463 // from ShouldSyncTab(..).
TEST_F(SessionsSyncManagerTest,ValidTabs)464 TEST_F(SessionsSyncManagerTest, ValidTabs) {
465 SyncedTabDelegateFake tab;
466
467 // A null entry shouldn't crash.
468 tab.AppendEntry(NULL);
469 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
470 tab.reset();
471
472 // A chrome:// entry isn't valid.
473 content::NavigationEntry* entry(content::NavigationEntry::Create());
474 entry->SetVirtualURL(GURL("chrome://preferences/"));
475 tab.AppendEntry(entry);
476 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
477
478
479 // A file:// entry isn't valid, even in addition to another entry.
480 content::NavigationEntry* entry2(content::NavigationEntry::Create());
481 entry2->SetVirtualURL(GURL("file://bla"));
482 tab.AppendEntry(entry2);
483 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
484
485 // Add a valid scheme entry to tab, making the tab valid.
486 content::NavigationEntry* entry3(content::NavigationEntry::Create());
487 entry3->SetVirtualURL(GURL("http://www.google.com"));
488 tab.AppendEntry(entry3);
489 EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
490 }
491
492 // Make sure GetCurrentVirtualURL() returns the virtual URL of the pending
493 // entry if the current entry is pending.
TEST_F(SessionsSyncManagerTest,GetCurrentVirtualURLPending)494 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLPending) {
495 SyncedTabDelegateFake tab;
496 content::NavigationEntry* entry(content::NavigationEntry::Create());
497 entry->SetVirtualURL(GURL("http://www.google.com"));
498 tab.AppendEntry(entry);
499 EXPECT_EQ(entry->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab));
500 }
501
502 // Make sure GetCurrentVirtualURL() returns the virtual URL of the current
503 // entry if the current entry is non-pending.
TEST_F(SessionsSyncManagerTest,GetCurrentVirtualURLNonPending)504 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLNonPending) {
505 SyncedTabDelegateFake tab;
506 content::NavigationEntry* entry(content::NavigationEntry::Create());
507 entry->SetVirtualURL(GURL("http://www.google.com"));
508 tab.AppendEntry(entry);
509 EXPECT_EQ(entry->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab));
510 }
511
512 static const base::Time kTime0 = base::Time::FromInternalValue(100);
513 static const base::Time kTime1 = base::Time::FromInternalValue(110);
514 static const base::Time kTime2 = base::Time::FromInternalValue(120);
515 static const base::Time kTime3 = base::Time::FromInternalValue(130);
516 static const base::Time kTime4 = base::Time::FromInternalValue(140);
517 static const base::Time kTime5 = base::Time::FromInternalValue(150);
518 static const base::Time kTime6 = base::Time::FromInternalValue(160);
519 static const base::Time kTime7 = base::Time::FromInternalValue(170);
520 static const base::Time kTime8 = base::Time::FromInternalValue(180);
521 static const base::Time kTime9 = base::Time::FromInternalValue(190);
522
523 // Populate the mock tab delegate with some data and navigation
524 // entries and make sure that setting a SessionTab from it preserves
525 // those entries (and clobbers any existing data).
TEST_F(SessionsSyncManagerTest,SetSessionTabFromDelegate)526 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegate) {
527 // Create a tab with three valid entries.
528 SyncedTabDelegateFake tab;
529 content::NavigationEntry* entry1(content::NavigationEntry::Create());
530 entry1->SetVirtualURL(GURL("http://www.google.com"));
531 entry1->SetTimestamp(kTime1);
532 entry1->SetHttpStatusCode(200);
533 content::NavigationEntry* entry2(content::NavigationEntry::Create());
534 entry2->SetVirtualURL(GURL("http://www.noodle.com"));
535 entry2->SetTimestamp(kTime2);
536 entry2->SetHttpStatusCode(201);
537 content::NavigationEntry* entry3(content::NavigationEntry::Create());
538 entry3->SetVirtualURL(GURL("http://www.doodle.com"));
539 entry3->SetTimestamp(kTime3);
540 entry3->SetHttpStatusCode(202);
541
542 tab.AppendEntry(entry1);
543 tab.AppendEntry(entry2);
544 tab.AppendEntry(entry3);
545 tab.set_current_entry_index(2);
546
547 SessionTab session_tab;
548 session_tab.window_id.set_id(1);
549 session_tab.tab_id.set_id(1);
550 session_tab.tab_visual_index = 1;
551 session_tab.current_navigation_index = 1;
552 session_tab.pinned = true;
553 session_tab.extension_app_id = "app id";
554 session_tab.user_agent_override = "override";
555 session_tab.timestamp = kTime5;
556 session_tab.navigations.push_back(
557 SerializedNavigationEntryTestHelper::CreateNavigation(
558 "http://www.example.com", "Example"));
559 session_tab.session_storage_persistent_id = "persistent id";
560 manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab);
561
562 EXPECT_EQ(0, session_tab.window_id.id());
563 EXPECT_EQ(0, session_tab.tab_id.id());
564 EXPECT_EQ(0, session_tab.tab_visual_index);
565 EXPECT_EQ(2, session_tab.current_navigation_index);
566 EXPECT_FALSE(session_tab.pinned);
567 EXPECT_TRUE(session_tab.extension_app_id.empty());
568 EXPECT_TRUE(session_tab.user_agent_override.empty());
569 EXPECT_EQ(kTime4, session_tab.timestamp);
570 ASSERT_EQ(3u, session_tab.navigations.size());
571 EXPECT_EQ(entry1->GetVirtualURL(),
572 session_tab.navigations[0].virtual_url());
573 EXPECT_EQ(entry2->GetVirtualURL(),
574 session_tab.navigations[1].virtual_url());
575 EXPECT_EQ(entry3->GetVirtualURL(),
576 session_tab.navigations[2].virtual_url());
577 EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
578 EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
579 EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
580 EXPECT_EQ(200, session_tab.navigations[0].http_status_code());
581 EXPECT_EQ(201, session_tab.navigations[1].http_status_code());
582 EXPECT_EQ(202, session_tab.navigations[2].http_status_code());
583 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
584 session_tab.navigations[0].blocked_state());
585 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
586 session_tab.navigations[1].blocked_state());
587 EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
588 session_tab.navigations[2].blocked_state());
589 EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
590 }
591
592 // Ensure the current_navigation_index gets set properly when the navigation
593 // stack gets trucated to +/- 6 entries.
TEST_F(SessionsSyncManagerTest,SetSessionTabFromDelegateNavigationIndex)594 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegateNavigationIndex) {
595 SyncedTabDelegateFake tab;
596 content::NavigationEntry* entry0(content::NavigationEntry::Create());
597 entry0->SetVirtualURL(GURL("http://www.google.com"));
598 entry0->SetTimestamp(kTime0);
599 entry0->SetHttpStatusCode(200);
600 content::NavigationEntry* entry1(content::NavigationEntry::Create());
601 entry1->SetVirtualURL(GURL("http://www.zoogle.com"));
602 entry1->SetTimestamp(kTime1);
603 entry1->SetHttpStatusCode(200);
604 content::NavigationEntry* entry2(content::NavigationEntry::Create());
605 entry2->SetVirtualURL(GURL("http://www.noogle.com"));
606 entry2->SetTimestamp(kTime2);
607 entry2->SetHttpStatusCode(200);
608 content::NavigationEntry* entry3(content::NavigationEntry::Create());
609 entry3->SetVirtualURL(GURL("http://www.doogle.com"));
610 entry3->SetTimestamp(kTime3);
611 entry3->SetHttpStatusCode(200);
612 content::NavigationEntry* entry4(content::NavigationEntry::Create());
613 entry4->SetVirtualURL(GURL("http://www.yoogle.com"));
614 entry4->SetTimestamp(kTime4);
615 entry4->SetHttpStatusCode(200);
616 content::NavigationEntry* entry5(content::NavigationEntry::Create());
617 entry5->SetVirtualURL(GURL("http://www.foogle.com"));
618 entry5->SetTimestamp(kTime5);
619 entry5->SetHttpStatusCode(200);
620 content::NavigationEntry* entry6(content::NavigationEntry::Create());
621 entry6->SetVirtualURL(GURL("http://www.boogle.com"));
622 entry6->SetTimestamp(kTime6);
623 entry6->SetHttpStatusCode(200);
624 content::NavigationEntry* entry7(content::NavigationEntry::Create());
625 entry7->SetVirtualURL(GURL("http://www.moogle.com"));
626 entry7->SetTimestamp(kTime7);
627 entry7->SetHttpStatusCode(200);
628 content::NavigationEntry* entry8(content::NavigationEntry::Create());
629 entry8->SetVirtualURL(GURL("http://www.poogle.com"));
630 entry8->SetTimestamp(kTime8);
631 entry8->SetHttpStatusCode(200);
632 content::NavigationEntry* entry9(content::NavigationEntry::Create());
633 entry9->SetVirtualURL(GURL("http://www.roogle.com"));
634 entry9->SetTimestamp(kTime9);
635 entry9->SetHttpStatusCode(200);
636
637 tab.AppendEntry(entry0);
638 tab.AppendEntry(entry1);
639 tab.AppendEntry(entry2);
640 tab.AppendEntry(entry3);
641 tab.AppendEntry(entry4);
642 tab.AppendEntry(entry5);
643 tab.AppendEntry(entry6);
644 tab.AppendEntry(entry7);
645 tab.AppendEntry(entry8);
646 tab.AppendEntry(entry9);
647 tab.set_current_entry_index(8);
648
649 SessionTab session_tab;
650 manager()->SetSessionTabFromDelegate(tab, kTime9, &session_tab);
651
652 EXPECT_EQ(6, session_tab.current_navigation_index);
653 ASSERT_EQ(8u, session_tab.navigations.size());
654 EXPECT_EQ(entry2->GetVirtualURL(),
655 session_tab.navigations[0].virtual_url());
656 EXPECT_EQ(entry3->GetVirtualURL(),
657 session_tab.navigations[1].virtual_url());
658 EXPECT_EQ(entry4->GetVirtualURL(),
659 session_tab.navigations[2].virtual_url());
660 }
661
662 // Ensure the current_navigation_index gets set to the end of the navigation
663 // stack if the current navigation is invalid.
TEST_F(SessionsSyncManagerTest,SetSessionTabFromDelegateCurrentInvalid)664 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegateCurrentInvalid) {
665 SyncedTabDelegateFake tab;
666 content::NavigationEntry* entry0(content::NavigationEntry::Create());
667 entry0->SetVirtualURL(GURL("http://www.google.com"));
668 entry0->SetTimestamp(kTime0);
669 entry0->SetHttpStatusCode(200);
670 content::NavigationEntry* entry1(content::NavigationEntry::Create());
671 entry1->SetVirtualURL(GURL(""));
672 entry1->SetTimestamp(kTime1);
673 entry1->SetHttpStatusCode(200);
674 content::NavigationEntry* entry2(content::NavigationEntry::Create());
675 entry2->SetVirtualURL(GURL("http://www.noogle.com"));
676 entry2->SetTimestamp(kTime2);
677 entry2->SetHttpStatusCode(200);
678 content::NavigationEntry* entry3(content::NavigationEntry::Create());
679 entry3->SetVirtualURL(GURL("http://www.doogle.com"));
680 entry3->SetTimestamp(kTime3);
681 entry3->SetHttpStatusCode(200);
682
683 tab.AppendEntry(entry0);
684 tab.AppendEntry(entry1);
685 tab.AppendEntry(entry2);
686 tab.AppendEntry(entry3);
687 tab.set_current_entry_index(1);
688
689 SessionTab session_tab;
690 manager()->SetSessionTabFromDelegate(tab, kTime9, &session_tab);
691
692 EXPECT_EQ(2, session_tab.current_navigation_index);
693 ASSERT_EQ(3u, session_tab.navigations.size());
694 }
695
696 // Tests that for supervised users blocked navigations are recorded and marked
697 // as such, while regular navigations are marked as allowed.
TEST_F(SessionsSyncManagerTest,BlockedNavigations)698 TEST_F(SessionsSyncManagerTest, BlockedNavigations) {
699 SyncedTabDelegateFake tab;
700 content::NavigationEntry* entry1(content::NavigationEntry::Create());
701 entry1->SetVirtualURL(GURL("http://www.google.com"));
702 entry1->SetTimestamp(kTime1);
703 tab.AppendEntry(entry1);
704
705 content::NavigationEntry* entry2 = content::NavigationEntry::Create();
706 entry2->SetVirtualURL(GURL("http://blocked.com/foo"));
707 entry2->SetTimestamp(kTime2);
708 content::NavigationEntry* entry3 = content::NavigationEntry::Create();
709 entry3->SetVirtualURL(GURL("http://evil.com"));
710 entry3->SetTimestamp(kTime3);
711 ScopedVector<const content::NavigationEntry> blocked_navigations;
712 blocked_navigations.push_back(entry2);
713 blocked_navigations.push_back(entry3);
714
715 tab.set_is_supervised(true);
716 tab.set_blocked_navigations(&blocked_navigations.get());
717
718 SessionTab session_tab;
719 session_tab.window_id.set_id(1);
720 session_tab.tab_id.set_id(1);
721 session_tab.tab_visual_index = 1;
722 session_tab.current_navigation_index = 1;
723 session_tab.pinned = true;
724 session_tab.extension_app_id = "app id";
725 session_tab.user_agent_override = "override";
726 session_tab.timestamp = kTime5;
727 session_tab.navigations.push_back(
728 SerializedNavigationEntryTestHelper::CreateNavigation(
729 "http://www.example.com", "Example"));
730 session_tab.session_storage_persistent_id = "persistent id";
731 manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab);
732
733 EXPECT_EQ(0, session_tab.window_id.id());
734 EXPECT_EQ(0, session_tab.tab_id.id());
735 EXPECT_EQ(0, session_tab.tab_visual_index);
736 EXPECT_EQ(0, session_tab.current_navigation_index);
737 EXPECT_FALSE(session_tab.pinned);
738 EXPECT_TRUE(session_tab.extension_app_id.empty());
739 EXPECT_TRUE(session_tab.user_agent_override.empty());
740 EXPECT_EQ(kTime4, session_tab.timestamp);
741 ASSERT_EQ(3u, session_tab.navigations.size());
742 EXPECT_EQ(entry1->GetVirtualURL(),
743 session_tab.navigations[0].virtual_url());
744 EXPECT_EQ(entry2->GetVirtualURL(),
745 session_tab.navigations[1].virtual_url());
746 EXPECT_EQ(entry3->GetVirtualURL(),
747 session_tab.navigations[2].virtual_url());
748 EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
749 EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
750 EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
751 EXPECT_EQ(SerializedNavigationEntry::STATE_ALLOWED,
752 session_tab.navigations[0].blocked_state());
753 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
754 session_tab.navigations[1].blocked_state());
755 EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
756 session_tab.navigations[2].blocked_state());
757 EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
758 }
759
760 // Tests that the local session header objects is created properly in
761 // presence of no other session activity, once and only once.
TEST_F(SessionsSyncManagerTest,MergeLocalSessionNoTabs)762 TEST_F(SessionsSyncManagerTest, MergeLocalSessionNoTabs) {
763 syncer::SyncChangeList out;
764 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
765 EXPECT_FALSE(manager()->current_machine_tag().empty());
766
767 EXPECT_EQ(2U, out.size());
768 EXPECT_TRUE(out[0].IsValid());
769 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
770 const SyncData data(out[0].sync_data());
771 EXPECT_EQ(manager()->current_machine_tag(),
772 syncer::SyncDataLocal(data).GetTag());
773 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
774 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
775 EXPECT_TRUE(specifics.has_header());
776 const sync_pb::SessionHeader& header_s = specifics.header();
777 EXPECT_TRUE(header_s.has_device_type());
778 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
779 EXPECT_EQ(0, header_s.window_size());
780
781 EXPECT_TRUE(out[1].IsValid());
782 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
783 const SyncData data_2(out[1].sync_data());
784 EXPECT_EQ(manager()->current_machine_tag(),
785 syncer::SyncDataLocal(data_2).GetTag());
786 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
787 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
788 EXPECT_TRUE(specifics2.has_header());
789 const sync_pb::SessionHeader& header_s2 = specifics2.header();
790 EXPECT_EQ(0, header_s2.window_size());
791
792 // Now take that header node and feed it in as input.
793 SyncData d(SyncData::CreateRemoteData(
794 1,
795 data.GetSpecifics(),
796 base::Time(),
797 syncer::AttachmentIdList(),
798 syncer::AttachmentServiceProxyForTest::Create()));
799 syncer::SyncDataList in(&d, &d + 1);
800 out.clear();
801 SessionsSyncManager manager2(profile(), local_device(), NewDummyRouter());
802 syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
803 syncer::SESSIONS, in,
804 scoped_ptr<syncer::SyncChangeProcessor>(
805 new TestSyncProcessorStub(&out)),
806 scoped_ptr<syncer::SyncErrorFactory>(
807 new syncer::SyncErrorFactoryMock()));
808 ASSERT_FALSE(result.error().IsSet());
809
810 EXPECT_EQ(1U, out.size());
811 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
812 EXPECT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
813 }
814
815 // Ensure model association associates the pre-existing tabs.
TEST_F(SessionsSyncManagerTest,SwappedOutOnRestore)816 TEST_F(SessionsSyncManagerTest, SwappedOutOnRestore) {
817 AddTab(browser(), GURL("http://foo1"));
818 NavigateAndCommitActiveTab(GURL("http://foo2"));
819 AddTab(browser(), GURL("http://bar1"));
820 NavigateAndCommitActiveTab(GURL("http://bar2"));
821 AddTab(browser(), GURL("http://baz1"));
822 NavigateAndCommitActiveTab(GURL("http://baz2"));
823 const int kRestoredTabId = 1337;
824 const int kNewTabId = 2468;
825
826 syncer::SyncDataList in;
827 syncer::SyncChangeList out;
828 InitWithSyncDataTakeOutput(in, &out);
829
830 // Should be one header add, 3 tab add/update pairs, one header update.
831 ASSERT_EQ(8U, out.size());
832
833 // For input, we set up:
834 // * one "normal" fully loaded tab
835 // * one "frozen" tab with no WebContents and a tab_id change
836 // * one "frozen" tab with no WebContents and no tab_id change
837 SyncData t0(SyncData::CreateRemoteData(
838 1,
839 out[2].sync_data().GetSpecifics(),
840 base::Time(),
841 syncer::AttachmentIdList(),
842 syncer::AttachmentServiceProxyForTest::Create()));
843 sync_pb::EntitySpecifics entity(out[4].sync_data().GetSpecifics());
844 entity.mutable_session()->mutable_tab()->set_tab_id(kRestoredTabId);
845 SyncData t1(SyncData::CreateRemoteData(
846 2,
847 entity,
848 base::Time(),
849 syncer::AttachmentIdList(),
850 syncer::AttachmentServiceProxyForTest::Create()));
851 SyncData t2(SyncData::CreateRemoteData(
852 3,
853 out[6].sync_data().GetSpecifics(),
854 base::Time(),
855 syncer::AttachmentIdList(),
856 syncer::AttachmentServiceProxyForTest::Create()));
857 in.push_back(t0);
858 in.push_back(t1);
859 in.push_back(t2);
860 out.clear();
861 manager()->StopSyncing(syncer::SESSIONS);
862
863 const std::set<SyncedWindowDelegate*> windows(
864 SyncedWindowDelegate::GetSyncedWindowDelegates());
865 ASSERT_EQ(1U, windows.size());
866 SyncedTabDelegateFake t1_override, t2_override;
867 t1_override.SetSyncId(1); // No WebContents by default.
868 t2_override.SetSyncId(2); // No WebContents by default.
869 SyncedWindowDelegateOverride window_override(*windows.begin());
870 window_override.OverrideTabAt(1, &t1_override, kNewTabId);
871 window_override.OverrideTabAt(2, &t2_override,
872 t2.GetSpecifics().session().tab().tab_id());
873 std::set<SyncedWindowDelegate*> delegates;
874 delegates.insert(&window_override);
875 scoped_ptr<TestSyncedWindowDelegatesGetter> getter(
876 new TestSyncedWindowDelegatesGetter(delegates));
877 manager()->synced_window_getter_.reset(getter.release());
878
879 syncer::SyncMergeResult result = manager()->MergeDataAndStartSyncing(
880 syncer::SESSIONS, in,
881 scoped_ptr<syncer::SyncChangeProcessor>(
882 new TestSyncProcessorStub(&out)),
883 scoped_ptr<syncer::SyncErrorFactory>(
884 new syncer::SyncErrorFactoryMock()));
885
886 // There should be two changes, one for the fully associated tab, and
887 // one for the tab_id update to t1. t2 shouldn't need to be updated.
888 ASSERT_EQ(2U, FilterOutLocalHeaderChanges(&out)->size());
889 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
890 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
891 EXPECT_EQ(kNewTabId,
892 out[1].sync_data().GetSpecifics().session().tab().tab_id());
893
894 // Verify TabLinks.
895 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
896 ASSERT_EQ(3U, tab_map.size());
897 int t2_tab_id = t2.GetSpecifics().session().tab().tab_id();
898 EXPECT_EQ(2, tab_map.find(t2_tab_id)->second->tab_node_id());
899 EXPECT_EQ(1, tab_map.find(kNewTabId)->second->tab_node_id());
900 int t0_tab_id = out[0].sync_data().GetSpecifics().session().tab().tab_id();
901 EXPECT_EQ(0, tab_map.find(t0_tab_id)->second->tab_node_id());
902 // TODO(tim): Once bug 337057 is fixed, we can issue an OnLocalTabModified
903 // from here (using an override similar to above) to return a new tab id
904 // and verify that we don't see any node creations in the SyncChangeProcessor
905 // (similar to how SessionsSyncManagerTest.OnLocalTabModified works.)
906 }
907
908 // Tests MergeDataAndStartSyncing with sync data but no local data.
TEST_F(SessionsSyncManagerTest,MergeWithInitialForeignSession)909 TEST_F(SessionsSyncManagerTest, MergeWithInitialForeignSession) {
910 std::string tag = "tag1";
911
912 SessionID::id_type n1[] = {5, 10, 13, 17};
913 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
914 std::vector<sync_pb::SessionSpecifics> tabs1;
915 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
916 tag, tab_list1, &tabs1));
917 // Add a second window.
918 SessionID::id_type n2[] = {7, 15, 18, 20};
919 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
920 helper()->AddWindowSpecifics(1, tab_list2, &meta);
921
922 // Set up initial data.
923 syncer::SyncDataList initial_data;
924 sync_pb::EntitySpecifics entity;
925 entity.mutable_session()->CopyFrom(meta);
926 initial_data.push_back(SyncData::CreateRemoteData(
927 1,
928 entity,
929 base::Time(),
930 syncer::AttachmentIdList(),
931 syncer::AttachmentServiceProxyForTest::Create()));
932 AddTabsToSyncDataList(tabs1, &initial_data);
933
934 for (size_t i = 0; i < tab_list2.size(); ++i) {
935 sync_pb::EntitySpecifics entity;
936 helper()->BuildTabSpecifics(tag, 0, tab_list2[i],
937 entity.mutable_session());
938 initial_data.push_back(SyncData::CreateRemoteData(
939 i + 10,
940 entity,
941 base::Time(),
942 syncer::AttachmentIdList(),
943 syncer::AttachmentServiceProxyForTest::Create()));
944 }
945
946 syncer::SyncChangeList output;
947 InitWithSyncDataTakeOutput(initial_data, &output);
948 EXPECT_TRUE(FilterOutLocalHeaderChanges(&output)->empty());
949
950 std::vector<const SyncedSession*> foreign_sessions;
951 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
952 ASSERT_EQ(1U, foreign_sessions.size());
953 std::vector<std::vector<SessionID::id_type> > session_reference;
954 session_reference.push_back(tab_list1);
955 session_reference.push_back(tab_list2);
956 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
957 }
958
959 // This is a combination of MergeWithInitialForeignSession and
960 // MergeLocalSessionExistingTabs. We repeat some checks performed in each of
961 // those tests to ensure the common mixed scenario works.
TEST_F(SessionsSyncManagerTest,MergeWithLocalAndForeignTabs)962 TEST_F(SessionsSyncManagerTest, MergeWithLocalAndForeignTabs) {
963 // Local.
964 AddTab(browser(), GURL("http://foo1"));
965 NavigateAndCommitActiveTab(GURL("http://foo2"));
966
967 // Foreign.
968 std::string tag = "tag1";
969 SessionID::id_type n1[] = {5, 10, 13, 17};
970 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
971 std::vector<sync_pb::SessionSpecifics> tabs1;
972 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
973 tag, tab_list1, &tabs1));
974 syncer::SyncDataList foreign_data;
975 sync_pb::EntitySpecifics entity;
976 entity.mutable_session()->CopyFrom(meta);
977 foreign_data.push_back(SyncData::CreateRemoteData(
978 1,
979 entity,
980 base::Time(),
981 syncer::AttachmentIdList(),
982 syncer::AttachmentServiceProxyForTest::Create()));
983 AddTabsToSyncDataList(tabs1, &foreign_data);
984
985 syncer::SyncChangeList output;
986 InitWithSyncDataTakeOutput(foreign_data, &output);
987 ASSERT_EQ(4U, output.size());
988
989 // Verify the local header.
990 EXPECT_TRUE(output[0].IsValid());
991 EXPECT_EQ(SyncChange::ACTION_ADD, output[0].change_type());
992 const SyncData data(output[0].sync_data());
993 EXPECT_EQ(manager()->current_machine_tag(),
994 syncer::SyncDataLocal(data).GetTag());
995 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
996 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
997 EXPECT_TRUE(specifics.has_header());
998 const sync_pb::SessionHeader& header_s = specifics.header();
999 EXPECT_TRUE(header_s.has_device_type());
1000 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
1001 EXPECT_EQ(0, header_s.window_size());
1002
1003 // Verify the tab node creations and updates with content.
1004 for (int i = 1; i < 3; i++) {
1005 EXPECT_TRUE(output[i].IsValid());
1006 const SyncData data(output[i].sync_data());
1007 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(),
1008 manager()->current_machine_tag(), true));
1009 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1010 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1011 }
1012 EXPECT_EQ(SyncChange::ACTION_ADD, output[1].change_type());
1013 EXPECT_EQ(SyncChange::ACTION_UPDATE, output[2].change_type());
1014 EXPECT_TRUE(output[2].sync_data().GetSpecifics().session().has_tab());
1015
1016 // Verify the header was updated to reflect window state.
1017 EXPECT_TRUE(output[3].IsValid());
1018 EXPECT_EQ(SyncChange::ACTION_UPDATE, output[3].change_type());
1019 const SyncData data_2(output[3].sync_data());
1020 EXPECT_EQ(manager()->current_machine_tag(),
1021 syncer::SyncDataLocal(data_2).GetTag());
1022 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
1023 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
1024 EXPECT_TRUE(specifics2.has_header());
1025 const sync_pb::SessionHeader& header_s2 = specifics2.header();
1026 EXPECT_EQ(1, header_s2.window_size());
1027
1028 // Verify foreign data.
1029 std::vector<const SyncedSession*> foreign_sessions;
1030 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1031 std::vector<std::vector<SessionID::id_type> > session_reference;
1032 session_reference.push_back(tab_list1);
1033 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
1034 // There should be one and only one foreign session. If VerifySyncedSession
1035 // was successful above this EXPECT call ensures the local session didn't
1036 // get mistakenly added to foreign tracking (Similar to ExistingTabs test).
1037 EXPECT_EQ(1U, foreign_sessions.size());
1038 }
1039
1040 // Tests the common scenario. Merge with both local and foreign session data
1041 // followed by updates flowing from sync and local.
TEST_F(SessionsSyncManagerTest,UpdatesAfterMixedMerge)1042 TEST_F(SessionsSyncManagerTest, UpdatesAfterMixedMerge) {
1043 // Add local and foreign data.
1044 AddTab(browser(), GURL("http://foo1"));
1045 NavigateAndCommitActiveTab(GURL("http://foo2"));
1046
1047 std::string tag1 = "tag1";
1048 syncer::SyncDataList foreign_data1;
1049 std::vector<std::vector<SessionID::id_type> > meta1_reference;
1050 sync_pb::SessionSpecifics meta1;
1051
1052 SessionID::id_type n1[] = {5, 10, 13, 17};
1053 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
1054 meta1_reference.push_back(tab_list1);
1055 std::vector<sync_pb::SessionSpecifics> tabs1;
1056 meta1 = helper()->BuildForeignSession(tag1, tab_list1, &tabs1);
1057 sync_pb::EntitySpecifics entity;
1058 entity.mutable_session()->CopyFrom(meta1);
1059 foreign_data1.push_back(SyncData::CreateRemoteData(
1060 1,
1061 entity,
1062 base::Time(),
1063 syncer::AttachmentIdList(),
1064 syncer::AttachmentServiceProxyForTest::Create()));
1065 AddTabsToSyncDataList(tabs1, &foreign_data1);
1066
1067 syncer::SyncChangeList output1;
1068 InitWithSyncDataTakeOutput(foreign_data1, &output1);
1069 ASSERT_EQ(4U, output1.size());
1070
1071 // Add a second window to the foreign session.
1072 // TODO(tim): Bug 98892. Add local window too when observers are hooked up.
1073 SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
1074 std::vector<SessionID::id_type> tab_list2(
1075 tab_nums2, tab_nums2 + arraysize(tab_nums2));
1076 meta1_reference.push_back(tab_list2);
1077 helper()->AddWindowSpecifics(1, tab_list2, &meta1);
1078 std::vector<sync_pb::SessionSpecifics> tabs2;
1079 tabs2.resize(tab_list2.size());
1080 for (size_t i = 0; i < tab_list2.size(); ++i) {
1081 helper()->BuildTabSpecifics(tag1, 0, tab_list2[i], &tabs2[i]);
1082 }
1083
1084 syncer::SyncChangeList changes;
1085 changes.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE));
1086 AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes);
1087 manager()->ProcessSyncChanges(FROM_HERE, changes);
1088 changes.clear();
1089
1090 // Check that the foreign session was associated and retrieve the data.
1091 std::vector<const SyncedSession*> foreign_sessions;
1092 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1093 ASSERT_EQ(1U, foreign_sessions.size());
1094 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
1095 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
1096 helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0]));
1097
1098 // Add a new foreign session.
1099 std::string tag2 = "tag2";
1100 SessionID::id_type n2[] = {107, 115};
1101 std::vector<SessionID::id_type> tag2_tab_list(n2, n2 + arraysize(n2));
1102 std::vector<sync_pb::SessionSpecifics> tag2_tabs;
1103 sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession(
1104 tag2, tag2_tab_list, &tag2_tabs));
1105 changes.push_back(MakeRemoteChange(100, meta2, SyncChange::ACTION_ADD));
1106 AddTabsToChangeList(tag2_tabs, SyncChange::ACTION_ADD, &changes);
1107
1108 manager()->ProcessSyncChanges(FROM_HERE, changes);
1109 changes.clear();
1110
1111 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1112 std::vector<std::vector<SessionID::id_type> > meta2_reference;
1113 meta2_reference.push_back(tag2_tab_list);
1114 ASSERT_EQ(2U, foreign_sessions.size());
1115 ASSERT_EQ(2U, foreign_sessions[1]->windows.find(0)->second->tabs.size());
1116 helper()->VerifySyncedSession(tag2, meta2_reference, *(foreign_sessions[1]));
1117 foreign_sessions.clear();
1118
1119 // Remove a tab from a window.
1120 meta1_reference[0].pop_back();
1121 tab_list1.pop_back();
1122 sync_pb::SessionWindow* win = meta1.mutable_header()->mutable_window(0);
1123 win->clear_tab();
1124 for (std::vector<int>::const_iterator iter = tab_list1.begin();
1125 iter != tab_list1.end(); ++iter) {
1126 win->add_tab(*iter);
1127 }
1128 syncer::SyncChangeList removal;
1129 removal.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE));
1130 AddTabsToChangeList(tabs1, SyncChange::ACTION_UPDATE, &removal);
1131 manager()->ProcessSyncChanges(FROM_HERE, removal);
1132
1133 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1134 ASSERT_EQ(2U, foreign_sessions.size());
1135 ASSERT_EQ(3U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
1136 helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0]));
1137 }
1138
1139 // Tests that this SyncSessionManager knows how to delete foreign sessions
1140 // if it wants to.
TEST_F(SessionsSyncManagerTest,DeleteForeignSession)1141 TEST_F(SessionsSyncManagerTest, DeleteForeignSession) {
1142 InitWithNoSyncData();
1143 std::string tag = "tag1";
1144 syncer::SyncChangeList changes;
1145
1146 std::vector<const SyncedSession*> foreign_sessions;
1147 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1148 manager()->DeleteForeignSessionInternal(tag, &changes);
1149 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1150 EXPECT_TRUE(changes.empty());
1151
1152 // Fill an instance of session specifics with a foreign session's data.
1153 std::vector<sync_pb::SessionSpecifics> tabs;
1154 SessionID::id_type n1[] = {5, 10, 13, 17};
1155 std::vector<SessionID::id_type> tab_nums1(n1, n1 + arraysize(n1));
1156 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1157 tag, tab_nums1, &tabs));
1158
1159 // Update associator with the session's meta node, window, and tabs.
1160 manager()->UpdateTrackerWithForeignSession(meta, base::Time());
1161 for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs.begin();
1162 iter != tabs.end(); ++iter) {
1163 manager()->UpdateTrackerWithForeignSession(*iter, base::Time());
1164 }
1165 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1166 ASSERT_EQ(1U, foreign_sessions.size());
1167
1168 // Now delete the foreign session.
1169 manager()->DeleteForeignSessionInternal(tag, &changes);
1170 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1171
1172 EXPECT_EQ(5U, changes.size());
1173 std::set<std::string> expected_tags(&tag, &tag + 1);
1174 for (int i = 0; i < 5; i++)
1175 expected_tags.insert(TabNodePool::TabIdToTag(tag, i));
1176
1177 for (int i = 0; i < 5; i++) {
1178 SCOPED_TRACE(changes[i].ToString());
1179 EXPECT_TRUE(changes[i].IsValid());
1180 EXPECT_EQ(SyncChange::ACTION_DELETE, changes[i].change_type());
1181 EXPECT_TRUE(changes[i].sync_data().IsValid());
1182 EXPECT_EQ(1U,
1183 expected_tags.erase(
1184 syncer::SyncDataLocal(changes[i].sync_data()).GetTag()));
1185 }
1186 }
1187
1188 // Write a foreign session to a node, with the tabs arriving first, and then
1189 // retrieve it.
TEST_F(SessionsSyncManagerTest,WriteForeignSessionToNodeTabsFirst)1190 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeTabsFirst) {
1191 InitWithNoSyncData();
1192
1193 // Fill an instance of session specifics with a foreign session's data.
1194 std::string tag = "tag1";
1195 SessionID::id_type nums1[] = {5, 10, 13, 17};
1196 std::vector<sync_pb::SessionSpecifics> tabs1;
1197 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
1198 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1199 tag, tab_list1, &tabs1));
1200
1201 syncer::SyncChangeList adds;
1202 // Add tabs for first window, then the meta node.
1203 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &adds);
1204 adds.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1205 manager()->ProcessSyncChanges(FROM_HERE, adds);
1206
1207 // Check that the foreign session was associated and retrieve the data.
1208 std::vector<const SyncedSession*> foreign_sessions;
1209 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1210 ASSERT_EQ(1U, foreign_sessions.size());
1211 std::vector<std::vector<SessionID::id_type> > session_reference;
1212 session_reference.push_back(tab_list1);
1213 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
1214 }
1215
1216 // Write a foreign session to a node with some tabs that never arrive.
TEST_F(SessionsSyncManagerTest,WriteForeignSessionToNodeMissingTabs)1217 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeMissingTabs) {
1218 InitWithNoSyncData();
1219
1220 // Fill an instance of session specifics with a foreign session's data.
1221 std::string tag = "tag1";
1222 SessionID::id_type nums1[] = {5, 10, 13, 17};
1223 std::vector<sync_pb::SessionSpecifics> tabs1;
1224 std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
1225 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1226 tag, tab_list1, &tabs1));
1227 // Add a second window, but this time only create two tab nodes, despite the
1228 // window expecting four tabs.
1229 SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
1230 std::vector<SessionID::id_type> tab_list2(
1231 tab_nums2, tab_nums2 + arraysize(tab_nums2));
1232 helper()->AddWindowSpecifics(1, tab_list2, &meta);
1233 std::vector<sync_pb::SessionSpecifics> tabs2;
1234 tabs2.resize(2);
1235 for (size_t i = 0; i < 2; ++i) {
1236 helper()->BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
1237 }
1238
1239 syncer::SyncChangeList changes;
1240 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1241 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1242 AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes);
1243 manager()->ProcessSyncChanges(FROM_HERE, changes);
1244 changes.clear();
1245
1246 // Check that the foreign session was associated and retrieve the data.
1247 std::vector<const SyncedSession*> foreign_sessions;
1248 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1249 ASSERT_EQ(1U, foreign_sessions.size());
1250 ASSERT_EQ(2U, foreign_sessions[0]->windows.size());
1251 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
1252 ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
1253
1254 // Close the second window.
1255 meta.mutable_header()->clear_window();
1256 helper()->AddWindowSpecifics(0, tab_list1, &meta);
1257 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_UPDATE));
1258 // Update associator with the session's meta node containing one window.
1259 manager()->ProcessSyncChanges(FROM_HERE, changes);
1260
1261 // Check that the foreign session was associated and retrieve the data.
1262 foreign_sessions.clear();
1263 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1264 ASSERT_EQ(1U, foreign_sessions.size());
1265 ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
1266 std::vector<std::vector<SessionID::id_type> > session_reference;
1267 session_reference.push_back(tab_list1);
1268 helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
1269 }
1270
1271 // Tests that the SessionsSyncManager can handle a remote client deleting
1272 // sync nodes that belong to this local session.
TEST_F(SessionsSyncManagerTest,ProcessRemoteDeleteOfLocalSession)1273 TEST_F(SessionsSyncManagerTest, ProcessRemoteDeleteOfLocalSession) {
1274 syncer::SyncChangeList out;
1275 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1276 ASSERT_EQ(2U, out.size());
1277 sync_pb::EntitySpecifics entity(out[0].sync_data().GetSpecifics());
1278 SyncData d(SyncData::CreateRemoteData(
1279 1,
1280 entity,
1281 base::Time(),
1282 syncer::AttachmentIdList(),
1283 syncer::AttachmentServiceProxyForTest::Create()));
1284 SetSyncData(syncer::SyncDataList(&d, &d + 1));
1285 out.clear();
1286
1287 syncer::SyncChangeList changes;
1288 changes.push_back(
1289 MakeRemoteChange(1, entity.session(), SyncChange::ACTION_DELETE));
1290 manager()->ProcessSyncChanges(FROM_HERE, changes);
1291 EXPECT_TRUE(manager()->local_tab_pool_out_of_sync_);
1292 EXPECT_TRUE(out.empty()); // ChangeProcessor shouldn't see any activity.
1293
1294 // This should trigger repair of the TabNodePool.
1295 const GURL foo1("http://foo/1");
1296 AddTab(browser(), foo1);
1297 EXPECT_FALSE(manager()->local_tab_pool_out_of_sync_);
1298
1299 // AddTab triggers two notifications, one for the tab insertion and one for
1300 // committing the NavigationEntry. The first notification results in a tab
1301 // we don't associate although we do update the header node. The second
1302 // notification triggers association of the tab, and the subsequent window
1303 // update. So we should see 4 changes at the SyncChangeProcessor.
1304 ASSERT_EQ(4U, out.size());
1305
1306 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
1307 ASSERT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
1308 EXPECT_EQ(SyncChange::ACTION_ADD, out[1].change_type());
1309 int tab_node_id = out[1].sync_data().GetSpecifics().session().tab_node_id();
1310 EXPECT_EQ(TabNodePool::TabIdToTag(
1311 manager()->current_machine_tag(), tab_node_id),
1312 syncer::SyncDataLocal(out[1].sync_data()).GetTag());
1313 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
1314 ASSERT_TRUE(out[2].sync_data().GetSpecifics().session().has_tab());
1315 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[3].change_type());
1316 ASSERT_TRUE(out[3].sync_data().GetSpecifics().session().has_header());
1317
1318 // Verify the actual content.
1319 const sync_pb::SessionHeader& session_header =
1320 out[3].sync_data().GetSpecifics().session().header();
1321 ASSERT_EQ(1, session_header.window_size());
1322 EXPECT_EQ(1, session_header.window(0).tab_size());
1323 const sync_pb::SessionTab& tab1 =
1324 out[2].sync_data().GetSpecifics().session().tab();
1325 ASSERT_EQ(1, tab1.navigation_size());
1326 EXPECT_EQ(foo1.spec(), tab1.navigation(0).virtual_url());
1327
1328 // Verify TabNodePool integrity.
1329 EXPECT_EQ(1U, manager()->local_tab_pool_.Capacity());
1330 EXPECT_TRUE(manager()->local_tab_pool_.Empty());
1331
1332 // Verify TabLinks.
1333 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
1334 ASSERT_EQ(1U, tab_map.size());
1335 int tab_id = out[2].sync_data().GetSpecifics().session().tab().tab_id();
1336 EXPECT_EQ(tab_node_id, tab_map.find(tab_id)->second->tab_node_id());
1337 }
1338
1339 // Test that receiving a session delete from sync removes the session
1340 // from tracking.
TEST_F(SessionsSyncManagerTest,ProcessForeignDelete)1341 TEST_F(SessionsSyncManagerTest, ProcessForeignDelete) {
1342 InitWithNoSyncData();
1343 SessionID::id_type n[] = {5};
1344 std::vector<sync_pb::SessionSpecifics> tabs1;
1345 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
1346 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1347 "tag1", tab_list, &tabs1));
1348
1349 syncer::SyncChangeList changes;
1350 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1351 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1352 manager()->ProcessSyncChanges(FROM_HERE, changes);
1353
1354 std::vector<const SyncedSession*> foreign_sessions;
1355 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1356 ASSERT_EQ(1U, foreign_sessions.size());
1357
1358 changes.clear();
1359 foreign_sessions.clear();
1360 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE));
1361 manager()->ProcessSyncChanges(FROM_HERE, changes);
1362
1363 EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1364 }
1365
1366 // TODO(shashishekhar): "Move this to TabNodePool unittests."
TEST_F(SessionsSyncManagerTest,SaveUnassociatedNodesForReassociation)1367 TEST_F(SessionsSyncManagerTest, SaveUnassociatedNodesForReassociation) {
1368 syncer::SyncChangeList changes;
1369 InitWithNoSyncData();
1370
1371 std::string local_tag = manager()->current_machine_tag();
1372 // Create a free node and then dissassociate sessions so that it ends up
1373 // unassociated.
1374 manager()->local_tab_pool_.GetFreeTabNode(&changes);
1375
1376 // Update the tab_id of the node, so that it is considered a valid
1377 // unassociated node otherwise it will be mistaken for a corrupted node and
1378 // will be deleted before being added to the tab node pool.
1379 sync_pb::EntitySpecifics entity(changes[0].sync_data().GetSpecifics());
1380 entity.mutable_session()->mutable_tab()->set_tab_id(1);
1381 SyncData d(SyncData::CreateRemoteData(
1382 1,
1383 entity,
1384 base::Time(),
1385 syncer::AttachmentIdList(),
1386 syncer::AttachmentServiceProxyForTest::Create()));
1387 syncer::SyncDataList in(&d, &d + 1);
1388 changes.clear();
1389 SessionsSyncManager manager2(profile(), local_device(), NewDummyRouter());
1390 syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
1391 syncer::SESSIONS, in,
1392 scoped_ptr<syncer::SyncChangeProcessor>(
1393 new TestSyncProcessorStub(&changes)),
1394 scoped_ptr<syncer::SyncErrorFactory>(
1395 new syncer::SyncErrorFactoryMock()));
1396 ASSERT_FALSE(result.error().IsSet());
1397 EXPECT_TRUE(FilterOutLocalHeaderChanges(&changes)->empty());
1398 }
1399
TEST_F(SessionsSyncManagerTest,MergeDeletesCorruptNode)1400 TEST_F(SessionsSyncManagerTest, MergeDeletesCorruptNode) {
1401 syncer::SyncChangeList changes;
1402 InitWithNoSyncData();
1403
1404 std::string local_tag = manager()->current_machine_tag();
1405 int tab_node_id = manager()->local_tab_pool_.GetFreeTabNode(&changes);
1406 SyncData d(SyncData::CreateRemoteData(
1407 1,
1408 changes[0].sync_data().GetSpecifics(),
1409 base::Time(),
1410 syncer::AttachmentIdList(),
1411 syncer::AttachmentServiceProxyForTest::Create()));
1412 syncer::SyncDataList in(&d, &d + 1);
1413 changes.clear();
1414 TearDown();
1415 SetUp();
1416 InitWithSyncDataTakeOutput(in, &changes);
1417 EXPECT_EQ(1U, FilterOutLocalHeaderChanges(&changes)->size());
1418 EXPECT_EQ(SyncChange::ACTION_DELETE, changes[0].change_type());
1419 EXPECT_EQ(TabNodePool::TabIdToTag(local_tag, tab_node_id),
1420 syncer::SyncDataLocal(changes[0].sync_data()).GetTag());
1421 }
1422
1423 // Test that things work if a tab is initially ignored.
TEST_F(SessionsSyncManagerTest,AssociateWindowsDontReloadTabs)1424 TEST_F(SessionsSyncManagerTest, AssociateWindowsDontReloadTabs) {
1425 syncer::SyncChangeList out;
1426 // Go to a URL that is ignored by session syncing.
1427 AddTab(browser(), GURL("chrome://preferences/"));
1428 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1429 ASSERT_EQ(2U, out.size()); // Header add and update.
1430 EXPECT_EQ(
1431 0,
1432 out[1].sync_data().GetSpecifics().session().header().window_size());
1433 out.clear();
1434
1435 // Go to a sync-interesting URL.
1436 NavigateAndCommitActiveTab(GURL("http://foo2"));
1437
1438 EXPECT_EQ(3U, out.size()); // Tab add, update, and header update.
1439
1440 EXPECT_TRUE(
1441 StartsWithASCII(syncer::SyncDataLocal(out[0].sync_data()).GetTag(),
1442 manager()->current_machine_tag(),
1443 true));
1444 EXPECT_EQ(manager()->current_machine_tag(),
1445 out[0].sync_data().GetSpecifics().session().session_tag());
1446 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
1447
1448 EXPECT_TRUE(
1449 StartsWithASCII(syncer::SyncDataLocal(out[1].sync_data()).GetTag(),
1450 manager()->current_machine_tag(),
1451 true));
1452 EXPECT_EQ(manager()->current_machine_tag(),
1453 out[1].sync_data().GetSpecifics().session().session_tag());
1454 EXPECT_TRUE(out[1].sync_data().GetSpecifics().session().has_tab());
1455 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
1456
1457 EXPECT_TRUE(out[2].IsValid());
1458 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
1459 const SyncData data(out[2].sync_data());
1460 EXPECT_EQ(manager()->current_machine_tag(),
1461 syncer::SyncDataLocal(data).GetTag());
1462 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1463 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1464 EXPECT_TRUE(specifics.has_header());
1465 const sync_pb::SessionHeader& header_s = specifics.header();
1466 EXPECT_EQ(1, header_s.window_size());
1467 EXPECT_EQ(1, header_s.window(0).tab_size());
1468 }
1469
1470 // Tests that the SyncSessionManager responds to local tab events properly.
TEST_F(SessionsSyncManagerTest,OnLocalTabModified)1471 TEST_F(SessionsSyncManagerTest, OnLocalTabModified) {
1472 syncer::SyncChangeList out;
1473 // Init with no local data, relies on MergeLocalSessionNoTabs.
1474 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1475 ASSERT_FALSE(manager()->current_machine_tag().empty());
1476 ASSERT_EQ(2U, out.size());
1477
1478 // Copy the original header.
1479 sync_pb::EntitySpecifics header(out[0].sync_data().GetSpecifics());
1480 out.clear();
1481
1482 const GURL foo1("http://foo/1");
1483 const GURL foo2("http://foo/2");
1484 const GURL bar1("http://bar/1");
1485 const GURL bar2("http://bar/2");
1486 AddTab(browser(), foo1);
1487 NavigateAndCommitActiveTab(foo2);
1488 AddTab(browser(), bar1);
1489 NavigateAndCommitActiveTab(bar2);
1490
1491 // One add, one update for each AddTab.
1492 // One update for each NavigateAndCommit.
1493 // = 6 total tab updates.
1494 // One header update corresponding to each of those.
1495 // = 6 total header updates.
1496 // 12 total updates.
1497 ASSERT_EQ(12U, out.size());
1498
1499 // Verify the tab node creations and updates to ensure the SyncProcessor
1500 // sees the right operations.
1501 for (int i = 0; i < 12; i++) {
1502 SCOPED_TRACE(i);
1503 EXPECT_TRUE(out[i].IsValid());
1504 const SyncData data(out[i].sync_data());
1505 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(),
1506 manager()->current_machine_tag(), true));
1507 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1508 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1509 if (i % 6 == 0) {
1510 // First thing on an AddTab is a no-op header update for parented tab.
1511 EXPECT_EQ(header.SerializeAsString(),
1512 data.GetSpecifics().SerializeAsString());
1513 EXPECT_EQ(manager()->current_machine_tag(),
1514 syncer::SyncDataLocal(data).GetTag());
1515 } else if (i % 6 == 1) {
1516 // Next, the TabNodePool should create the tab node.
1517 EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
1518 EXPECT_EQ(TabNodePool::TabIdToTag(
1519 manager()->current_machine_tag(),
1520 data.GetSpecifics().session().tab_node_id()),
1521 syncer::SyncDataLocal(data).GetTag());
1522 } else if (i % 6 == 2) {
1523 // Then we see the tab update to the URL.
1524 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1525 EXPECT_EQ(TabNodePool::TabIdToTag(
1526 manager()->current_machine_tag(),
1527 data.GetSpecifics().session().tab_node_id()),
1528 syncer::SyncDataLocal(data).GetTag());
1529 ASSERT_TRUE(specifics.has_tab());
1530 } else if (i % 6 == 3) {
1531 // The header needs to be updated to reflect the new window state.
1532 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1533 EXPECT_TRUE(specifics.has_header());
1534 } else if (i % 6 == 4) {
1535 // Now we move on to NavigateAndCommit. Update the tab.
1536 EXPECT_EQ(SyncChange::ACTION_UPDATE, 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 ASSERT_TRUE(specifics.has_tab());
1542 } else if (i % 6 == 5) {
1543 // The header needs to be updated to reflect the new window state.
1544 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1545 ASSERT_TRUE(specifics.has_header());
1546 header = data.GetSpecifics();
1547 }
1548 }
1549
1550 // Verify the actual content to ensure sync sees the right data.
1551 // When it's all said and done, the header should reflect two tabs.
1552 const sync_pb::SessionHeader& session_header = header.session().header();
1553 ASSERT_EQ(1, session_header.window_size());
1554 EXPECT_EQ(2, session_header.window(0).tab_size());
1555
1556 // ASSERT_TRUEs above allow us to dive in freely here.
1557 // Verify first tab.
1558 const sync_pb::SessionTab& tab1_1 =
1559 out[2].sync_data().GetSpecifics().session().tab();
1560 ASSERT_EQ(1, tab1_1.navigation_size());
1561 EXPECT_EQ(foo1.spec(), tab1_1.navigation(0).virtual_url());
1562 const sync_pb::SessionTab& tab1_2 =
1563 out[4].sync_data().GetSpecifics().session().tab();
1564 ASSERT_EQ(2, tab1_2.navigation_size());
1565 EXPECT_EQ(foo1.spec(), tab1_2.navigation(0).virtual_url());
1566 EXPECT_EQ(foo2.spec(), tab1_2.navigation(1).virtual_url());
1567
1568 // Verify second tab.
1569 const sync_pb::SessionTab& tab2_1 =
1570 out[8].sync_data().GetSpecifics().session().tab();
1571 ASSERT_EQ(1, tab2_1.navigation_size());
1572 EXPECT_EQ(bar1.spec(), tab2_1.navigation(0).virtual_url());
1573 const sync_pb::SessionTab& tab2_2 =
1574 out[10].sync_data().GetSpecifics().session().tab();
1575 ASSERT_EQ(2, tab2_2.navigation_size());
1576 EXPECT_EQ(bar1.spec(), tab2_2.navigation(0).virtual_url());
1577 EXPECT_EQ(bar2.spec(), tab2_2.navigation(1).virtual_url());
1578 }
1579
1580 // Ensure model association associates the pre-existing tabs.
TEST_F(SessionsSyncManagerTest,MergeLocalSessionExistingTabs)1581 TEST_F(SessionsSyncManagerTest, MergeLocalSessionExistingTabs) {
1582 AddTab(browser(), GURL("http://foo1"));
1583 NavigateAndCommitActiveTab(GURL("http://foo2")); // Adds back entry.
1584 AddTab(browser(), GURL("http://bar1"));
1585 NavigateAndCommitActiveTab(GURL("http://bar2")); // Adds back entry.
1586
1587 syncer::SyncChangeList out;
1588 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1589 ASSERT_EQ(6U, out.size());
1590
1591 // Check that this machine's data is not included in the foreign windows.
1592 std::vector<const SyncedSession*> foreign_sessions;
1593 ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
1594
1595 // Verify the header.
1596 EXPECT_TRUE(out[0].IsValid());
1597 EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
1598 const SyncData data(out[0].sync_data());
1599 EXPECT_EQ(manager()->current_machine_tag(),
1600 syncer::SyncDataLocal(data).GetTag());
1601 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1602 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1603 EXPECT_TRUE(specifics.has_header());
1604 const sync_pb::SessionHeader& header_s = specifics.header();
1605 EXPECT_TRUE(header_s.has_device_type());
1606 EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
1607 EXPECT_EQ(0, header_s.window_size());
1608
1609 // Verify the tab node creations and updates with content.
1610 for (int i = 1; i < 5; i++) {
1611 EXPECT_TRUE(out[i].IsValid());
1612 const SyncData data(out[i].sync_data());
1613 EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(),
1614 manager()->current_machine_tag(), true));
1615 const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
1616 EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
1617 if (i % 2 == 1) {
1618 EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
1619 } else {
1620 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
1621 EXPECT_TRUE(specifics.has_tab());
1622 }
1623 }
1624
1625 // Verify the header was updated to reflect new window state.
1626 EXPECT_TRUE(out[5].IsValid());
1627 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type());
1628 const SyncData data_2(out[5].sync_data());
1629 EXPECT_EQ(manager()->current_machine_tag(),
1630 syncer::SyncDataLocal(data_2).GetTag());
1631 const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
1632 EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
1633 EXPECT_TRUE(specifics2.has_header());
1634 const sync_pb::SessionHeader& header_s2 = specifics2.header();
1635 EXPECT_EQ(1, header_s2.window_size());
1636
1637 // Verify TabLinks.
1638 SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
1639 ASSERT_EQ(2U, tab_map.size());
1640 // Tabs are ordered by sessionid in tab_map, so should be able to traverse
1641 // the tree based on order of tabs created
1642 SessionsSyncManager::TabLinksMap::iterator iter = tab_map.begin();
1643 ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
1644 EXPECT_EQ(GURL("http://foo1"), iter->second->tab()->
1645 GetEntryAtIndex(0)->GetVirtualURL());
1646 EXPECT_EQ(GURL("http://foo2"), iter->second->tab()->
1647 GetEntryAtIndex(1)->GetVirtualURL());
1648 iter++;
1649 ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
1650 EXPECT_EQ(GURL("http://bar1"), iter->second->tab()->
1651 GetEntryAtIndex(0)->GetVirtualURL());
1652 EXPECT_EQ(GURL("http://bar2"), iter->second->tab()->
1653 GetEntryAtIndex(1)->GetVirtualURL());
1654 }
1655
1656 // Test garbage collection of stale foreign sessions.
TEST_F(SessionsSyncManagerTest,DoGarbageCollection)1657 TEST_F(SessionsSyncManagerTest, DoGarbageCollection) {
1658 // Fill two instances of session specifics with a foreign session's data.
1659 std::string tag1 = "tag1";
1660 SessionID::id_type n1[] = {5, 10, 13, 17};
1661 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
1662 std::vector<sync_pb::SessionSpecifics> tabs1;
1663 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1664 tag1, tab_list1, &tabs1));
1665 std::string tag2 = "tag2";
1666 SessionID::id_type n2[] = {8, 15, 18, 20};
1667 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
1668 std::vector<sync_pb::SessionSpecifics> tabs2;
1669 sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession(
1670 tag2, tab_list2, &tabs2));
1671 // Set the modification time for tag1 to be 21 days ago, tag2 to 5 days ago.
1672 base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21);
1673 base::Time tag2_time = base::Time::Now() - base::TimeDelta::FromDays(5);
1674
1675 syncer::SyncDataList foreign_data;
1676 sync_pb::EntitySpecifics entity1, entity2;
1677 entity1.mutable_session()->CopyFrom(meta);
1678 entity2.mutable_session()->CopyFrom(meta2);
1679 foreign_data.push_back(SyncData::CreateRemoteData(
1680 1,
1681 entity1,
1682 tag1_time,
1683 syncer::AttachmentIdList(),
1684 syncer::AttachmentServiceProxyForTest::Create()));
1685 foreign_data.push_back(SyncData::CreateRemoteData(
1686 1,
1687 entity2,
1688 tag2_time,
1689 syncer::AttachmentIdList(),
1690 syncer::AttachmentServiceProxyForTest::Create()));
1691 AddTabsToSyncDataList(tabs1, &foreign_data);
1692 AddTabsToSyncDataList(tabs2, &foreign_data);
1693
1694 syncer::SyncChangeList output;
1695 InitWithSyncDataTakeOutput(foreign_data, &output);
1696 ASSERT_EQ(2U, output.size());
1697 output.clear();
1698
1699 // Check that the foreign session was associated and retrieve the data.
1700 std::vector<const SyncedSession*> foreign_sessions;
1701 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1702 ASSERT_EQ(2U, foreign_sessions.size());
1703 foreign_sessions.clear();
1704
1705 // Now garbage collect and verify the non-stale session is still there.
1706 manager()->DoGarbageCollection();
1707 ASSERT_EQ(5U, output.size());
1708 EXPECT_EQ(SyncChange::ACTION_DELETE, output[0].change_type());
1709 const SyncData data(output[0].sync_data());
1710 EXPECT_EQ(tag1, syncer::SyncDataLocal(data).GetTag());
1711 for (int i = 1; i < 5; i++) {
1712 EXPECT_EQ(SyncChange::ACTION_DELETE, output[i].change_type());
1713 const SyncData data(output[i].sync_data());
1714 EXPECT_EQ(TabNodePool::TabIdToTag(tag1, i),
1715 syncer::SyncDataLocal(data).GetTag());
1716 }
1717
1718 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1719 ASSERT_EQ(1U, foreign_sessions.size());
1720 std::vector<std::vector<SessionID::id_type> > session_reference;
1721 session_reference.push_back(tab_list2);
1722 helper()->VerifySyncedSession(tag2, session_reference,
1723 *(foreign_sessions[0]));
1724 }
1725
1726 // Test that an update to a previously considered "stale" session,
1727 // prior to garbage collection, will save the session from deletion.
TEST_F(SessionsSyncManagerTest,GarbageCollectionHonoursUpdate)1728 TEST_F(SessionsSyncManagerTest, GarbageCollectionHonoursUpdate) {
1729 std::string tag1 = "tag1";
1730 SessionID::id_type n1[] = {5, 10, 13, 17};
1731 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
1732 std::vector<sync_pb::SessionSpecifics> tabs1;
1733 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1734 tag1, tab_list1, &tabs1));
1735 syncer::SyncDataList foreign_data;
1736 sync_pb::EntitySpecifics entity1;
1737 base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21);
1738 entity1.mutable_session()->CopyFrom(meta);
1739 foreign_data.push_back(SyncData::CreateRemoteData(
1740 1,
1741 entity1,
1742 tag1_time,
1743 syncer::AttachmentIdList(),
1744 syncer::AttachmentServiceProxyForTest::Create()));
1745 AddTabsToSyncDataList(tabs1, &foreign_data);
1746 syncer::SyncChangeList output;
1747 InitWithSyncDataTakeOutput(foreign_data, &output);
1748 ASSERT_EQ(2U, output.size());
1749
1750 // Update to a non-stale time.
1751 sync_pb::EntitySpecifics update_entity;
1752 update_entity.mutable_session()->CopyFrom(tabs1[0]);
1753 syncer::SyncChangeList changes;
1754 changes.push_back(
1755 syncer::SyncChange(FROM_HERE,
1756 SyncChange::ACTION_UPDATE,
1757 syncer::SyncData::CreateRemoteData(
1758 1,
1759 update_entity,
1760 base::Time::Now(),
1761 syncer::AttachmentIdList(),
1762 syncer::AttachmentServiceProxyForTest::Create())));
1763 manager()->ProcessSyncChanges(FROM_HERE, changes);
1764
1765 // Check that the foreign session was associated and retrieve the data.
1766 std::vector<const SyncedSession*> foreign_sessions;
1767 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1768 ASSERT_EQ(1U, foreign_sessions.size());
1769 foreign_sessions.clear();
1770
1771 // Verify the now non-stale session does not get deleted.
1772 manager()->DoGarbageCollection();
1773 ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
1774 ASSERT_EQ(1U, foreign_sessions.size());
1775 std::vector<std::vector<SessionID::id_type> > session_reference;
1776 session_reference.push_back(tab_list1);
1777 helper()->VerifySyncedSession(
1778 tag1, session_reference, *(foreign_sessions[0]));
1779 }
1780
1781 // Test that swapping WebContents for a tab is properly observed and handled
1782 // by the SessionsSyncManager.
TEST_F(SessionsSyncManagerTest,CheckPrerenderedWebContentsSwap)1783 TEST_F(SessionsSyncManagerTest, CheckPrerenderedWebContentsSwap) {
1784 AddTab(browser(), GURL("http://foo1"));
1785 NavigateAndCommitActiveTab(GURL("http://foo2"));
1786
1787 syncer::SyncChangeList out;
1788 InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
1789 ASSERT_EQ(4U, out.size()); // Header, tab ADD, tab UPDATE, header UPDATE.
1790
1791 // To simulate WebContents swap during prerendering, create new WebContents
1792 // and swap with old WebContents.
1793 scoped_ptr<content::WebContents> old_web_contents;
1794 old_web_contents.reset(browser()->tab_strip_model()->GetActiveWebContents());
1795
1796 // Create new WebContents, with the required tab helpers.
1797 WebContents* new_web_contents = WebContents::CreateWithSessionStorage(
1798 WebContents::CreateParams(profile()),
1799 old_web_contents->GetController().GetSessionStorageNamespaceMap());
1800 SessionTabHelper::CreateForWebContents(new_web_contents);
1801 TabContentsSyncedTabDelegate::CreateForWebContents(new_web_contents);
1802 new_web_contents->GetController()
1803 .CopyStateFrom(old_web_contents->GetController());
1804
1805 // Swap the WebContents.
1806 int index = browser()->tab_strip_model()->GetIndexOfWebContents(
1807 old_web_contents.get());
1808 browser()->tab_strip_model()->ReplaceWebContentsAt(index, new_web_contents);
1809
1810 ASSERT_EQ(9U, out.size());
1811 EXPECT_EQ(SyncChange::ACTION_ADD, out[4].change_type());
1812 EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type());
1813
1814 // Navigate away.
1815 NavigateAndCommitActiveTab(GURL("http://bar2"));
1816
1817 // Delete old WebContents. This should not crash.
1818 old_web_contents.reset();
1819
1820 // Try more navigations and verify output size. This can also reveal
1821 // bugs (leaks) on memcheck bots if the SessionSyncManager
1822 // didn't properly clean up the tab pool or session tracker.
1823 NavigateAndCommitActiveTab(GURL("http://bar3"));
1824
1825 AddTab(browser(), GURL("http://bar4"));
1826 NavigateAndCommitActiveTab(GURL("http://bar5"));
1827 ASSERT_EQ(19U, out.size());
1828 }
1829
1830 namespace {
1831 class SessionNotificationObserver : public content::NotificationObserver {
1832 public:
SessionNotificationObserver()1833 SessionNotificationObserver() : notified_of_update_(false),
1834 notified_of_refresh_(false) {
1835 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
1836 content::NotificationService::AllSources());
1837 registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
1838 content::NotificationService::AllSources());
1839 }
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)1840 virtual void Observe(int type,
1841 const content::NotificationSource& source,
1842 const content::NotificationDetails& details) OVERRIDE {
1843 switch (type) {
1844 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
1845 notified_of_update_ = true;
1846 break;
1847 case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL:
1848 notified_of_refresh_ = true;
1849 break;
1850 default:
1851 NOTREACHED();
1852 break;
1853 }
1854 }
notified_of_update() const1855 bool notified_of_update() const { return notified_of_update_; }
notified_of_refresh() const1856 bool notified_of_refresh() const { return notified_of_refresh_; }
Reset()1857 void Reset() {
1858 notified_of_update_ = false;
1859 notified_of_refresh_ = false;
1860 }
1861 private:
1862 content::NotificationRegistrar registrar_;
1863 bool notified_of_update_;
1864 bool notified_of_refresh_;
1865 };
1866 } // namespace
1867
1868 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when processing
1869 // sync changes.
TEST_F(SessionsSyncManagerTest,NotifiedOfUpdates)1870 TEST_F(SessionsSyncManagerTest, NotifiedOfUpdates) {
1871 SessionNotificationObserver observer;
1872 ASSERT_FALSE(observer.notified_of_update());
1873 InitWithNoSyncData();
1874
1875 SessionID::id_type n[] = {5};
1876 std::vector<sync_pb::SessionSpecifics> tabs1;
1877 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
1878 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1879 "tag1", tab_list, &tabs1));
1880
1881 syncer::SyncChangeList changes;
1882 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1883 manager()->ProcessSyncChanges(FROM_HERE, changes);
1884 EXPECT_TRUE(observer.notified_of_update());
1885
1886 changes.clear();
1887 observer.Reset();
1888 AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
1889 manager()->ProcessSyncChanges(FROM_HERE, changes);
1890 EXPECT_TRUE(observer.notified_of_update());
1891
1892 changes.clear();
1893 observer.Reset();
1894 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE));
1895 manager()->ProcessSyncChanges(FROM_HERE, changes);
1896 EXPECT_TRUE(observer.notified_of_update());
1897 }
1898
1899 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when handling
1900 // local hide/removal of foreign session.
TEST_F(SessionsSyncManagerTest,NotifiedOfLocalRemovalOfForeignSession)1901 TEST_F(SessionsSyncManagerTest, NotifiedOfLocalRemovalOfForeignSession) {
1902 InitWithNoSyncData();
1903 const std::string tag("tag1");
1904 SessionID::id_type n[] = {5};
1905 std::vector<sync_pb::SessionSpecifics> tabs1;
1906 std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
1907 sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
1908 tag, tab_list, &tabs1));
1909
1910 syncer::SyncChangeList changes;
1911 changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
1912 manager()->ProcessSyncChanges(FROM_HERE, changes);
1913
1914 SessionNotificationObserver observer;
1915 ASSERT_FALSE(observer.notified_of_update());
1916 manager()->DeleteForeignSession(tag);
1917 ASSERT_TRUE(observer.notified_of_update());
1918 }
1919
1920 #if defined(OS_ANDROID) || defined(OS_IOS)
1921 // Tests that opening the other devices page triggers a session sync refresh.
1922 // This page only exists on mobile platforms today; desktop has a
1923 // search-enhanced NTP without other devices.
TEST_F(SessionsSyncManagerTest,NotifiedOfRefresh)1924 TEST_F(SessionsSyncManagerTest, NotifiedOfRefresh) {
1925 SessionNotificationObserver observer;
1926 ASSERT_FALSE(observer.notified_of_refresh());
1927 InitWithNoSyncData();
1928 AddTab(browser(), GURL("http://foo1"));
1929 EXPECT_FALSE(observer.notified_of_refresh());
1930 NavigateAndCommitActiveTab(GURL("chrome://newtab/#open_tabs"));
1931 EXPECT_TRUE(observer.notified_of_refresh());
1932 }
1933 #endif // defined(OS_ANDROID) || defined(OS_IOS)
1934
1935 // Tests receipt of duplicate tab IDs in the same window. This should never
1936 // happen, but we want to make sure the client won't do anything bad if it does
1937 // receive such garbage input data.
TEST_F(SessionsSyncManagerTest,ReceiveDuplicateTabInSameWindow)1938 TEST_F(SessionsSyncManagerTest, ReceiveDuplicateTabInSameWindow) {
1939 std::string tag = "tag1";
1940
1941 // Reuse tab ID 10 in an attempt to trigger bad behavior.
1942 SessionID::id_type n1[] = {5, 10, 10, 17};
1943 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
1944 std::vector<sync_pb::SessionSpecifics> tabs1;
1945 sync_pb::SessionSpecifics meta(
1946 helper()->BuildForeignSession(tag, tab_list1, &tabs1));
1947
1948 // Set up initial data.
1949 syncer::SyncDataList initial_data;
1950 sync_pb::EntitySpecifics entity;
1951 entity.mutable_session()->CopyFrom(meta);
1952 initial_data.push_back(SyncData::CreateRemoteData(
1953 1,
1954 entity,
1955 base::Time(),
1956 syncer::AttachmentIdList(),
1957 syncer::AttachmentServiceProxyForTest::Create()));
1958 AddTabsToSyncDataList(tabs1, &initial_data);
1959
1960 syncer::SyncChangeList output;
1961 InitWithSyncDataTakeOutput(initial_data, &output);
1962 }
1963
1964 // Tests receipt of duplicate tab IDs for the same session. The duplicate tab
1965 // ID is present in two different windows. A client can't be expected to do
1966 // anything reasonable with this input, but we can expect that it doesn't
1967 // crash.
TEST_F(SessionsSyncManagerTest,ReceiveDuplicateTabInOtherWindow)1968 TEST_F(SessionsSyncManagerTest, ReceiveDuplicateTabInOtherWindow) {
1969 std::string tag = "tag1";
1970
1971 SessionID::id_type n1[] = {5, 10, 17};
1972 std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
1973 std::vector<sync_pb::SessionSpecifics> tabs1;
1974 sync_pb::SessionSpecifics meta(
1975 helper()->BuildForeignSession(tag, tab_list1, &tabs1));
1976
1977 // Add a second window. Tab ID 10 is a duplicate.
1978 SessionID::id_type n2[] = {10, 18, 20};
1979 std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
1980 helper()->AddWindowSpecifics(1, tab_list2, &meta);
1981
1982 // Set up initial data.
1983 syncer::SyncDataList initial_data;
1984 sync_pb::EntitySpecifics entity;
1985 entity.mutable_session()->CopyFrom(meta);
1986 initial_data.push_back(SyncData::CreateRemoteData(
1987 1,
1988 entity,
1989 base::Time(),
1990 syncer::AttachmentIdList(),
1991 syncer::AttachmentServiceProxyForTest::Create()));
1992 AddTabsToSyncDataList(tabs1, &initial_data);
1993
1994 for (size_t i = 0; i < tab_list2.size(); ++i) {
1995 sync_pb::EntitySpecifics entity;
1996 helper()->BuildTabSpecifics(tag, 0, tab_list2[i], entity.mutable_session());
1997 initial_data.push_back(SyncData::CreateRemoteData(
1998 i + 10,
1999 entity,
2000 base::Time(),
2001 syncer::AttachmentIdList(),
2002 syncer::AttachmentServiceProxyForTest::Create()));
2003 }
2004
2005 syncer::SyncChangeList output;
2006 InitWithSyncDataTakeOutput(initial_data, &output);
2007 }
2008
2009 } // namespace browser_sync
2010