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