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