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