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