Home | History | Annotate | Download | only in sync
      1 // Copyright (c) 2012 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 <map>
      6 #include <string>
      7 
      8 #include "base/bind.h"
      9 #include "base/bind_helpers.h"
     10 #include "base/callback.h"
     11 #include "base/compiler_specific.h"
     12 #include "base/files/scoped_temp_dir.h"
     13 #include "base/guid.h"
     14 #include "base/location.h"
     15 #include "base/memory/scoped_ptr.h"
     16 #include "base/run_loop.h"
     17 #include "base/stl_util.h"
     18 #include "base/time/time.h"
     19 #include "chrome/browser/chrome_notification_types.h"
     20 #include "chrome/browser/invalidation/invalidation_service_factory.h"
     21 #include "chrome/browser/sessions/session_tab_helper.h"
     22 #include "chrome/browser/signin/profile_oauth2_token_service.h"
     23 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
     24 #include "chrome/browser/signin/signin_manager.h"
     25 #include "chrome/browser/signin/signin_manager_factory.h"
     26 #include "chrome/browser/sync/abstract_profile_sync_service_test.h"
     27 #include "chrome/browser/sync/fake_oauth2_token_service.h"
     28 #include "chrome/browser/sync/glue/device_info.h"
     29 #include "chrome/browser/sync/glue/session_change_processor.h"
     30 #include "chrome/browser/sync/glue/session_data_type_controller.h"
     31 #include "chrome/browser/sync/glue/session_model_associator.h"
     32 #include "chrome/browser/sync/glue/session_sync_test_helper.h"
     33 #include "chrome/browser/sync/glue/sync_backend_host.h"
     34 #include "chrome/browser/sync/glue/synced_device_tracker.h"
     35 #include "chrome/browser/sync/glue/synced_tab_delegate.h"
     36 #include "chrome/browser/sync/glue/tab_node_pool.h"
     37 #include "chrome/browser/sync/profile_sync_components_factory_mock.h"
     38 #include "chrome/browser/sync/profile_sync_service_factory.h"
     39 #include "chrome/browser/sync/profile_sync_test_util.h"
     40 #include "chrome/browser/sync/test_profile_sync_service.h"
     41 #include "chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h"
     42 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     43 #include "chrome/test/base/browser_with_test_window_test.h"
     44 #include "chrome/test/base/testing_profile.h"
     45 #include "content/public/browser/navigation_controller.h"
     46 #include "content/public/browser/navigation_entry.h"
     47 #include "content/public/browser/notification_observer.h"
     48 #include "content/public/browser/notification_registrar.h"
     49 #include "content/public/browser/notification_service.h"
     50 #include "content/public/browser/web_contents.h"
     51 #include "content/public/test/test_browser_thread.h"
     52 #include "google_apis/gaia/gaia_constants.h"
     53 #include "net/url_request/test_url_fetcher_factory.h"
     54 #include "sync/internal_api/public/base/model_type.h"
     55 #include "sync/internal_api/public/change_record.h"
     56 #include "sync/internal_api/public/read_node.h"
     57 #include "sync/internal_api/public/read_transaction.h"
     58 #include "sync/internal_api/public/test/test_user_share.h"
     59 #include "sync/internal_api/public/write_node.h"
     60 #include "sync/internal_api/public/write_transaction.h"
     61 #include "sync/protocol/session_specifics.pb.h"
     62 #include "sync/protocol/sync.pb.h"
     63 #include "testing/gmock/include/gmock/gmock.h"
     64 #include "testing/gtest/include/gtest/gtest.h"
     65 #include "ui/base/ui_base_types.h"
     66 #include "url/gurl.h"
     67 
     68 using browser_sync::SessionChangeProcessor;
     69 using browser_sync::SessionDataTypeController;
     70 using browser_sync::SessionModelAssociator;
     71 using browser_sync::SyncBackendHost;
     72 using content::BrowserThread;
     73 using content::WebContents;
     74 using syncer::ChangeRecord;
     75 using testing::_;
     76 using testing::Return;
     77 
     78 namespace browser_sync {
     79 
     80 namespace {
     81 
     82 class FakeProfileSyncService : public TestProfileSyncService {
     83  public:
     84   FakeProfileSyncService(
     85       ProfileSyncComponentsFactory* factory,
     86       Profile* profile,
     87       SigninManagerBase* signin,
     88       ProfileOAuth2TokenService* oauth2_token_service,
     89       ProfileSyncService::StartBehavior behavior)
     90       : TestProfileSyncService(factory,
     91                                profile,
     92                                signin,
     93                                oauth2_token_service,
     94                                behavior) {}
     95   virtual ~FakeProfileSyncService() {}
     96 
     97   virtual scoped_ptr<DeviceInfo> GetLocalDeviceInfo() const OVERRIDE {
     98     return scoped_ptr<DeviceInfo>(new DeviceInfo(base::GenerateGUID(),
     99                                                  "client_name",
    100                                                  std::string(),
    101                                                  std::string(),
    102                                                  sync_pb::SyncEnums::TYPE_WIN));
    103   }
    104 };
    105 
    106 bool CompareMemoryToString(
    107     const std::string& str,
    108     const scoped_refptr<base::RefCountedMemory>& mem) {
    109   if (mem->size() != str.size())
    110     return false;
    111   for (size_t i = 0; i <mem->size(); ++i) {
    112     if (str[i] != *(mem->front() + i))
    113       return false;
    114   }
    115   return true;
    116 }
    117 
    118 }  // namespace
    119 
    120 class ProfileSyncServiceSessionTest
    121     : public BrowserWithTestWindowTest,
    122       public content::NotificationObserver {
    123  public:
    124   ProfileSyncServiceSessionTest()
    125       : window_bounds_(0, 1, 2, 3),
    126         notified_of_refresh_(false),
    127         notified_of_update_(false) {}
    128   ProfileSyncService* sync_service() { return sync_service_.get(); }
    129 
    130  protected:
    131   virtual TestingProfile* CreateProfile() OVERRIDE {
    132     TestingProfile::Builder builder;
    133     builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(),
    134                               FakeOAuth2TokenService::BuildTokenService);
    135     // Don't want the profile to create a real ProfileSyncService.
    136     builder.AddTestingFactory(ProfileSyncServiceFactory::GetInstance(), NULL);
    137     scoped_ptr<TestingProfile> profile(builder.Build());
    138     invalidation::InvalidationServiceFactory::GetInstance()->
    139         SetBuildOnlyFakeInvalidatorsForTest(true);
    140     return profile.release();
    141   }
    142 
    143   virtual void SetUp() {
    144     // BrowserWithTestWindowTest implementation.
    145     BrowserWithTestWindowTest::SetUp();
    146     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    147     registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
    148         content::NotificationService::AllSources());
    149     registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
    150         content::NotificationService::AllSources());
    151   }
    152 
    153   virtual void Observe(int type,
    154       const content::NotificationSource& source,
    155       const content::NotificationDetails& details) OVERRIDE {
    156     switch (type) {
    157       case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
    158         notified_of_update_ = true;
    159         break;
    160       case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL:
    161         notified_of_refresh_ = true;
    162         break;
    163       default:
    164         NOTREACHED();
    165         break;
    166     }
    167   }
    168 
    169   virtual void TearDown() {
    170     sync_service_->Shutdown();
    171     sync_service_.reset();
    172 
    173     // We need to destroy the profile before shutting down the threads, because
    174     // some of the ref counted objects in the profile depend on their
    175     // destruction on the io thread.
    176     DestroyBrowserAndProfile();
    177     ASSERT_FALSE(profile());
    178 
    179     // Pump messages posted by the sync core thread (which may end up
    180     // posting on the IO thread).
    181     base::RunLoop().RunUntilIdle();
    182     BrowserWithTestWindowTest::TearDown();
    183   }
    184 
    185   bool StartSyncService(const base::Closure& callback,
    186                         bool will_fail_association) {
    187     if (sync_service_)
    188       return false;
    189     SigninManagerBase* signin =
    190         SigninManagerFactory::GetForProfile(profile());
    191     signin->SetAuthenticatedUsername("test_user");
    192     ProfileOAuth2TokenService* oauth2_token_service =
    193         ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
    194     ProfileSyncComponentsFactoryMock* factory =
    195         new ProfileSyncComponentsFactoryMock();
    196     sync_service_.reset(new FakeProfileSyncService(
    197         factory,
    198         profile(),
    199         signin,
    200         oauth2_token_service,
    201         ProfileSyncService::AUTO_START));
    202     sync_service_->set_backend_init_callback(callback);
    203 
    204     // Register the session data type.
    205     SessionDataTypeController *dtc = new SessionDataTypeController(factory,
    206                                          profile(),
    207                                          sync_service_.get());
    208     sync_service_->RegisterDataTypeController(dtc);
    209 
    210     model_associator_ =
    211         new SessionModelAssociator(sync_service_.get(),
    212                                    true /* setup_for_test */);
    213     change_processor_ = new SessionChangeProcessor(
    214         dtc, model_associator_,
    215         true /* setup_for_test */);
    216     EXPECT_CALL(*factory, CreateSessionSyncComponents(_, _)).
    217         WillOnce(Return(ProfileSyncComponentsFactory::SyncComponents(
    218             model_associator_, change_processor_)));
    219     EXPECT_CALL(*factory, CreateDataTypeManager(_, _, _, _, _, _)).
    220         WillOnce(ReturnNewDataTypeManager());
    221 
    222     ProfileOAuth2TokenServiceFactory::GetForProfile(profile())
    223         ->UpdateCredentials("test_user", "oauth2_login_token");
    224     sync_service_->Initialize();
    225     base::MessageLoop::current()->Run();
    226     return true;
    227   }
    228 
    229   // Path used in testing.
    230   base::ScopedTempDir temp_dir_;
    231   SessionModelAssociator* model_associator_;
    232   SessionChangeProcessor* change_processor_;
    233   SessionID window_id_;
    234   scoped_ptr<TestProfileSyncService> sync_service_;
    235   const gfx::Rect window_bounds_;
    236   bool notified_of_refresh_;
    237   bool notified_of_update_;
    238   content::NotificationRegistrar registrar_;
    239   net::TestURLFetcherFactory fetcher_factory_;
    240   SessionSyncTestHelper helper_;
    241 };
    242 
    243 class CreateRootHelper {
    244  public:
    245   explicit CreateRootHelper(ProfileSyncServiceSessionTest* test)
    246       : callback_(base::Bind(&CreateRootHelper::CreateRootCallback,
    247                              base::Unretained(this), test)),
    248         success_(false) {
    249   }
    250 
    251   virtual ~CreateRootHelper() {}
    252 
    253   const base::Closure& callback() const { return callback_; }
    254   bool success() { return success_; }
    255 
    256  private:
    257   void CreateRootCallback(ProfileSyncServiceSessionTest* test) {
    258     success_ = syncer::TestUserShare::CreateRoot(
    259         syncer::SESSIONS, test->sync_service()->GetUserShare());
    260   }
    261 
    262   base::Closure callback_;
    263   bool success_;
    264 };
    265 
    266 // Test that we can write this machine's session to a node and retrieve it.
    267 TEST_F(ProfileSyncServiceSessionTest, WriteSessionToNode) {
    268   CreateRootHelper create_root(this);
    269   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    270   ASSERT_TRUE(create_root.success());
    271 
    272   // Check that the DataTypeController associated the models.
    273   bool has_nodes;
    274   ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
    275   ASSERT_TRUE(has_nodes);
    276   std::string machine_tag = model_associator_->GetCurrentMachineTag();
    277   int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
    278   ASSERT_NE(syncer::kInvalidId, sync_id);
    279 
    280   // Check that we can get the correct session specifics back from the node.
    281   syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
    282   syncer::ReadNode node(&trans);
    283   ASSERT_EQ(syncer::BaseNode::INIT_OK,
    284             node.InitByClientTagLookup(syncer::SESSIONS, machine_tag));
    285   const sync_pb::SessionSpecifics& specifics(node.GetSessionSpecifics());
    286   ASSERT_EQ(machine_tag, specifics.session_tag());
    287   ASSERT_TRUE(specifics.has_header());
    288   const sync_pb::SessionHeader& header_s = specifics.header();
    289   ASSERT_TRUE(header_s.has_device_type());
    290   ASSERT_EQ("client_name", header_s.client_name());
    291   ASSERT_EQ(0, header_s.window_size());
    292 }
    293 
    294 // Crashes sometimes on Windows, particularly XP.
    295 // See http://crbug.com/174951
    296 #if defined(OS_WIN)
    297 #define MAYBE_WriteFilledSessionToNode DISABLED_WriteFilledSessionToNode
    298 #else
    299 #define MAYBE_WriteFilledSessionToNode WriteFilledSessionToNode
    300 #endif  // defined(OS_WIN)
    301 
    302 // Test that we can fill this machine's session, write it to a node,
    303 // and then retrieve it.
    304 TEST_F(ProfileSyncServiceSessionTest, MAYBE_WriteFilledSessionToNode) {
    305   CreateRootHelper create_root(this);
    306   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    307   ASSERT_TRUE(create_root.success());
    308 
    309   // Check that the DataTypeController associated the models.
    310   bool has_nodes;
    311   ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
    312   ASSERT_TRUE(has_nodes);
    313   AddTab(browser(), GURL("http://foo/1"));
    314   NavigateAndCommitActiveTab(GURL("http://foo/2"));
    315   AddTab(browser(), GURL("http://bar/1"));
    316   NavigateAndCommitActiveTab(GURL("http://bar/2"));
    317 
    318   ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
    319   ASSERT_TRUE(has_nodes);
    320   std::string machine_tag = model_associator_->GetCurrentMachineTag();
    321   int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
    322   ASSERT_NE(syncer::kInvalidId, sync_id);
    323 
    324   // Check that this machine's data is not included in the foreign windows.
    325   std::vector<const SyncedSession*> foreign_sessions;
    326   ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    327   ASSERT_EQ(foreign_sessions.size(), 0U);
    328 
    329   // Get the tabs for this machine from the node and check that they were
    330   // filled.
    331   SessionModelAssociator::TabLinksMap tab_map =
    332       model_associator_->local_tab_map_;
    333   ASSERT_EQ(2U, tab_map.size());
    334   // Tabs are ordered by sessionid in tab_map, so should be able to traverse
    335   // the tree based on order of tabs created
    336   SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin();
    337   ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
    338   ASSERT_EQ(GURL("http://foo/1"), iter->second->tab()->
    339           GetEntryAtIndex(0)->GetVirtualURL());
    340   ASSERT_EQ(GURL("http://foo/2"), iter->second->tab()->
    341           GetEntryAtIndex(1)->GetVirtualURL());
    342   iter++;
    343   ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
    344   ASSERT_EQ(GURL("http://bar/1"), iter->second->tab()->
    345       GetEntryAtIndex(0)->GetVirtualURL());
    346   ASSERT_EQ(GURL("http://bar/2"), iter->second->tab()->
    347       GetEntryAtIndex(1)->GetVirtualURL());
    348 }
    349 
    350 // Test that we fail on a failed model association.
    351 TEST_F(ProfileSyncServiceSessionTest, FailModelAssociation) {
    352   ASSERT_TRUE(StartSyncService(base::Closure(), true));
    353   ASSERT_TRUE(sync_service_->HasUnrecoverableError());
    354 }
    355 
    356 // Write a foreign session to a node, and then retrieve it.
    357 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) {
    358   CreateRootHelper create_root(this);
    359   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    360   ASSERT_TRUE(create_root.success());
    361 
    362   // Check that the DataTypeController associated the models.
    363   bool has_nodes;
    364   ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
    365   ASSERT_TRUE(has_nodes);
    366 
    367   // Fill an instance of session specifics with a foreign session's data.
    368   std::string tag = "tag1";
    369   SessionID::id_type nums1[] = {5, 10, 13, 17};
    370   std::vector<sync_pb::SessionSpecifics> tabs1;
    371   std::vector<SessionID::id_type> tab_list1(nums1, nums1 + arraysize(nums1));
    372   sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
    373       tag, tab_list1, &tabs1));
    374 
    375   // Update associator with the session's meta node containing one window.
    376   model_associator_->AssociateForeignSpecifics(meta, base::Time());
    377   // Add tabs for the window.
    378   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
    379        iter != tabs1.end(); ++iter) {
    380     model_associator_->AssociateForeignSpecifics(*iter, base::Time());
    381   }
    382 
    383   // Check that the foreign session was associated and retrieve the data.
    384   std::vector<const SyncedSession*> foreign_sessions;
    385   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    386   ASSERT_EQ(1U, foreign_sessions.size());
    387   std::vector<std::vector<SessionID::id_type> > session_reference;
    388   session_reference.push_back(tab_list1);
    389   helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    390 }
    391 
    392 // Write a foreign session with one window to a node. Sync, then add a window.
    393 // Sync, then add a third window. Close the two windows.
    394 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeThreeWindows) {
    395   CreateRootHelper create_root(this);
    396   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    397   ASSERT_TRUE(create_root.success());
    398 
    399   // Build a foreign session with one window and four tabs.
    400   std::string tag = "tag1";
    401   SessionID::id_type nums1[] = {5, 10, 13, 17};
    402   std::vector<sync_pb::SessionSpecifics> tabs1;
    403   std::vector<SessionID::id_type> tab_list1(nums1, nums1 + arraysize(nums1));
    404   sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
    405       tag, tab_list1, &tabs1));
    406   // Update associator with the session's meta node containing one window.
    407   model_associator_->AssociateForeignSpecifics(meta, base::Time());
    408   // Add tabs for first window.
    409   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
    410        iter != tabs1.end(); ++iter) {
    411     model_associator_->AssociateForeignSpecifics(*iter, base::Time());
    412   }
    413 
    414   // Verify first window
    415   std::vector<const SyncedSession*> foreign_sessions;
    416   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    417   std::vector<std::vector<SessionID::id_type> > session_reference;
    418   session_reference.push_back(tab_list1);
    419   helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    420 
    421   // Add a second window.
    422   SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
    423   std::vector<SessionID::id_type> tab_list2(
    424       tab_nums2, tab_nums2 + arraysize(tab_nums2));
    425   helper_.AddWindowSpecifics(1, tab_list2, &meta);
    426   std::vector<sync_pb::SessionSpecifics> tabs2;
    427   tabs2.resize(tab_list2.size());
    428   for (size_t i = 0; i < tab_list2.size(); ++i) {
    429     helper_.BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
    430   }
    431   // Update associator with the session's meta node containing two windows.
    432   model_associator_->AssociateForeignSpecifics(meta, base::Time());
    433   // Add tabs for second window.
    434   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs2.begin();
    435        iter != tabs2.end(); ++iter) {
    436     model_associator_->AssociateForeignSpecifics(*iter, base::Time());
    437   }
    438 
    439   // Verify the two windows.
    440   foreign_sessions.clear();
    441   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    442   ASSERT_EQ(1U, foreign_sessions.size());
    443   session_reference.push_back(tab_list2);
    444   helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    445 
    446   // Add a third window.
    447   SessionID::id_type tab_nums3[] = {8, 16, 19, 21};
    448   std::vector<SessionID::id_type> tab_list3(
    449       tab_nums3, tab_nums3 + arraysize(tab_nums3));
    450   helper_.AddWindowSpecifics(2, tab_list3, &meta);
    451   std::vector<sync_pb::SessionSpecifics> tabs3;
    452   tabs3.resize(tab_list3.size());
    453   for (size_t i = 0; i < tab_list3.size(); ++i) {
    454     helper_.BuildTabSpecifics(tag, 0, tab_list3[i], &tabs3[i]);
    455   }
    456   // Update associator with the session's meta node containing three windows.
    457   model_associator_->AssociateForeignSpecifics(meta, base::Time());
    458   // Add tabs for third window.
    459   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs3.begin();
    460        iter != tabs3.end(); ++iter) {
    461     model_associator_->AssociateForeignSpecifics(*iter, base::Time());
    462   }
    463 
    464   // Verify the three windows
    465   foreign_sessions.clear();
    466   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    467   ASSERT_EQ(1U, foreign_sessions.size());
    468   session_reference.push_back(tab_list3);
    469   helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    470 
    471   // Close third window (by clearing and then not adding it back).
    472   meta.mutable_header()->clear_window();
    473   helper_.AddWindowSpecifics(0, tab_list1, &meta);
    474   helper_.AddWindowSpecifics(1, tab_list2, &meta);
    475   // Update associator with just the meta node, now containing only two windows.
    476   model_associator_->AssociateForeignSpecifics(meta, base::Time());
    477 
    478   // Verify first two windows are still there.
    479   foreign_sessions.clear();
    480   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    481   ASSERT_EQ(1U, foreign_sessions.size());
    482   session_reference.pop_back();  // Pop off the data for the third window.
    483   helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    484 
    485   // Close second window (by clearing and then not adding it back).
    486   meta.mutable_header()->clear_window();
    487   helper_.AddWindowSpecifics(0, tab_list1, &meta);
    488   // Update associator with just the meta node, now containing only one windows.
    489   model_associator_->AssociateForeignSpecifics(meta, base::Time());
    490 
    491   // Verify first window is still there.
    492   foreign_sessions.clear();
    493   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    494   ASSERT_EQ(1U, foreign_sessions.size());
    495   session_reference.pop_back();  // Pop off the data for the second window.
    496   helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    497 }
    498 
    499 // Write a foreign session to a node, with the tabs arriving first, and then
    500 // retrieve it.
    501 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeTabsFirst) {
    502   CreateRootHelper create_root(this);
    503   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    504   ASSERT_TRUE(create_root.success());
    505 
    506   // Fill an instance of session specifics with a foreign session's data.
    507   std::string tag = "tag1";
    508   SessionID::id_type nums1[] = {5, 10, 13, 17};
    509   std::vector<sync_pb::SessionSpecifics> tabs1;
    510   std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
    511   sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
    512       tag, tab_list1, &tabs1));
    513 
    514   // Add tabs for first window.
    515   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
    516        iter != tabs1.end(); ++iter) {
    517     model_associator_->AssociateForeignSpecifics(*iter, base::Time());
    518   }
    519   // Update associator with the session's meta node containing one window.
    520   model_associator_->AssociateForeignSpecifics(meta, base::Time());
    521 
    522   // Check that the foreign session was associated and retrieve the data.
    523   std::vector<const SyncedSession*> foreign_sessions;
    524   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    525   ASSERT_EQ(1U, foreign_sessions.size());
    526   std::vector<std::vector<SessionID::id_type> > session_reference;
    527   session_reference.push_back(tab_list1);
    528   helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    529 }
    530 
    531 // Write a foreign session to a node with some tabs that never arrive.
    532 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeMissingTabs) {
    533   CreateRootHelper create_root(this);
    534   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    535   ASSERT_TRUE(create_root.success());
    536 
    537   // Fill an instance of session specifics with a foreign session's data.
    538   std::string tag = "tag1";
    539   SessionID::id_type nums1[] = {5, 10, 13, 17};
    540   std::vector<sync_pb::SessionSpecifics> tabs1;
    541   std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
    542   sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
    543       tag, tab_list1, &tabs1));
    544   // Add a second window, but this time only create two tab nodes, despite the
    545   // window expecting four tabs.
    546   SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
    547   std::vector<SessionID::id_type> tab_list2(
    548       tab_nums2, tab_nums2 + arraysize(tab_nums2));
    549   helper_.AddWindowSpecifics(1, tab_list2, &meta);
    550   std::vector<sync_pb::SessionSpecifics> tabs2;
    551   tabs2.resize(2);
    552   for (size_t i = 0; i < 2; ++i) {
    553     helper_.BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
    554   }
    555 
    556   // Update associator with the session's meta node containing two windows.
    557   model_associator_->AssociateForeignSpecifics(meta, base::Time());
    558   // Add tabs for first window.
    559   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
    560        iter != tabs1.end(); ++iter) {
    561     model_associator_->AssociateForeignSpecifics(*iter, base::Time());
    562   }
    563   // Add tabs for second window.
    564   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs2.begin();
    565        iter != tabs2.end(); ++iter) {
    566     model_associator_->AssociateForeignSpecifics(*iter, base::Time());
    567   }
    568 
    569   // Check that the foreign session was associated and retrieve the data.
    570   std::vector<const SyncedSession*> foreign_sessions;
    571   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    572   ASSERT_EQ(1U, foreign_sessions.size());
    573   ASSERT_EQ(2U, foreign_sessions[0]->windows.size());
    574   ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
    575   ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
    576 
    577   // Close the second window.
    578   meta.mutable_header()->clear_window();
    579   helper_.AddWindowSpecifics(0, tab_list1, &meta);
    580 
    581   // Update associator with the session's meta node containing one window.
    582   model_associator_->AssociateForeignSpecifics(meta, base::Time());
    583 
    584   // Check that the foreign session was associated and retrieve the data.
    585   foreign_sessions.clear();
    586   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    587   ASSERT_EQ(1U, foreign_sessions.size());
    588   ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
    589   std::vector<std::vector<SessionID::id_type> > session_reference;
    590   session_reference.push_back(tab_list1);
    591   helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    592 }
    593 
    594 // Test the DataTypeController on update.
    595 TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionUpdate) {
    596   CreateRootHelper create_root(this);
    597   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    598   ASSERT_TRUE(create_root.success());
    599   int64 node_id = model_associator_->GetSyncIdFromSessionTag(
    600       model_associator_->GetCurrentMachineTag());
    601   ASSERT_FALSE(notified_of_update_);
    602   {
    603     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
    604     change_processor_->ApplyChangesFromSyncModel(
    605         &trans, 0,
    606         ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList(
    607             node_id, ChangeRecord::ACTION_UPDATE));
    608   }
    609   ASSERT_TRUE(notified_of_update_);
    610 }
    611 
    612 // Test the DataTypeController on add.
    613 TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionAdd) {
    614   CreateRootHelper create_root(this);
    615   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    616   ASSERT_TRUE(create_root.success());
    617 
    618   int64 node_id = model_associator_->GetSyncIdFromSessionTag(
    619       model_associator_->GetCurrentMachineTag());
    620   ASSERT_FALSE(notified_of_update_);
    621   {
    622     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
    623     change_processor_->ApplyChangesFromSyncModel(
    624         &trans, 0,
    625         ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList(
    626             node_id, ChangeRecord::ACTION_ADD));
    627   }
    628   ASSERT_TRUE(notified_of_update_);
    629 }
    630 
    631 // Test the DataTypeController on delete.
    632 TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionDelete) {
    633   CreateRootHelper create_root(this);
    634   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    635   ASSERT_TRUE(create_root.success());
    636 
    637   int64 node_id = model_associator_->GetSyncIdFromSessionTag(
    638       model_associator_->GetCurrentMachineTag());
    639   sync_pb::EntitySpecifics deleted_specifics;
    640   deleted_specifics.mutable_session()->set_session_tag("tag");
    641   ASSERT_FALSE(notified_of_update_);
    642   {
    643     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
    644     change_processor_->ApplyChangesFromSyncModel(
    645         &trans, 0,
    646         ProfileSyncServiceTestHelper::MakeSingletonDeletionChangeRecordList(
    647             node_id, deleted_specifics));
    648   }
    649   ASSERT_TRUE(notified_of_update_);
    650 }
    651 // Test the TabNodePool when it starts off empty.
    652 TEST_F(ProfileSyncServiceSessionTest, TabNodePoolEmpty) {
    653   CreateRootHelper create_root(this);
    654   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    655   ASSERT_TRUE(create_root.success());
    656 
    657   std::vector<int> node_ids;
    658   ASSERT_EQ(0U, model_associator_->local_tab_pool_.Capacity());
    659   ASSERT_TRUE(model_associator_->local_tab_pool_.Empty());
    660   ASSERT_TRUE(model_associator_->local_tab_pool_.Full());
    661   const size_t num_ids = 10;
    662   for (size_t i = 0; i < num_ids; ++i) {
    663     int id = model_associator_->local_tab_pool_.GetFreeTabNode();
    664     ASSERT_GT(id, TabNodePool::kInvalidTabNodeID);
    665     node_ids.push_back(id);
    666     // Associate with a tab node.
    667     model_associator_->local_tab_pool_.AssociateTabNode(id, i + 1);
    668   }
    669   ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity());
    670   ASSERT_TRUE(model_associator_->local_tab_pool_.Empty());
    671   ASSERT_FALSE(model_associator_->local_tab_pool_.Full());
    672   for (size_t i = 0; i < num_ids; ++i) {
    673     model_associator_->local_tab_pool_.FreeTabNode(node_ids[i]);
    674   }
    675   ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity());
    676   ASSERT_FALSE(model_associator_->local_tab_pool_.Empty());
    677   ASSERT_TRUE(model_associator_->local_tab_pool_.Full());
    678 }
    679 
    680 // TODO(jhorwich): Re-enable when crbug.com/121487 addressed
    681 TEST_F(ProfileSyncServiceSessionTest, TabNodePoolNonEmpty) {
    682   CreateRootHelper create_root(this);
    683   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    684   ASSERT_TRUE(create_root.success());
    685 
    686   const size_t num_starting_nodes = 3;
    687   for (size_t i = 0; i < num_starting_nodes; ++i) {
    688     size_t node_id = i + 1;
    689     model_associator_->local_tab_pool_.AddTabNode(node_id);
    690     model_associator_->local_tab_pool_.AssociateTabNode(node_id, i);
    691     model_associator_->local_tab_pool_.FreeTabNode(node_id);
    692   }
    693 
    694   std::vector<int> node_ids;
    695   ASSERT_EQ(num_starting_nodes, model_associator_->local_tab_pool_.Capacity());
    696   ASSERT_FALSE(model_associator_->local_tab_pool_.Empty());
    697   ASSERT_TRUE(model_associator_->local_tab_pool_.Full());
    698   const size_t num_ids = 10;
    699   for (size_t i = 0; i < num_ids; ++i) {
    700     int id = model_associator_->local_tab_pool_.GetFreeTabNode();
    701     ASSERT_GT(id, TabNodePool::kInvalidTabNodeID);
    702     node_ids.push_back(id);
    703     // Associate with a tab node.
    704     model_associator_->local_tab_pool_.AssociateTabNode(id, i + 1);
    705   }
    706   ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity());
    707   ASSERT_TRUE(model_associator_->local_tab_pool_.Empty());
    708   ASSERT_FALSE(model_associator_->local_tab_pool_.Full());
    709   for (size_t i = 0; i < num_ids; ++i) {
    710     model_associator_->local_tab_pool_.FreeTabNode(node_ids[i]);
    711   }
    712   ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity());
    713   ASSERT_FALSE(model_associator_->local_tab_pool_.Empty());
    714   ASSERT_TRUE(model_associator_->local_tab_pool_.Full());
    715 }
    716 
    717 // Write a foreign session to a node, and then delete it.
    718 TEST_F(ProfileSyncServiceSessionTest, DeleteForeignSession) {
    719   CreateRootHelper create_root(this);
    720   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    721   ASSERT_TRUE(create_root.success());
    722 
    723   // Check that the DataTypeController associated the models.
    724   bool has_nodes;
    725   ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
    726   ASSERT_TRUE(has_nodes);
    727 
    728   // A foreign session's tag.
    729   std::string tag = "tag1";
    730 
    731   // Should do nothing if the foreign session doesn't exist.
    732   std::vector<const SyncedSession*> foreign_sessions;
    733   ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    734   ASSERT_FALSE(notified_of_update_);
    735   model_associator_->DeleteForeignSession(tag);
    736   ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    737   // Verify that deleteForeignSession did not trigger the
    738   // NOTIFICATION_FOREIGN_SESSION_DISABLED notification.
    739   ASSERT_FALSE(notified_of_update_);
    740 
    741   // Fill an instance of session specifics with a foreign session's data.
    742   SessionID::id_type nums1[] = {5, 10, 13, 17};
    743   std::vector<sync_pb::SessionSpecifics> tabs1;
    744   std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
    745   sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
    746       tag, tab_list1, &tabs1));
    747 
    748   // Update associator with the session's meta node containing one window.
    749   model_associator_->AssociateForeignSpecifics(meta, base::Time());
    750   // Add tabs for the window.
    751   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
    752        iter != tabs1.end(); ++iter) {
    753     model_associator_->AssociateForeignSpecifics(*iter, base::Time());
    754   }
    755 
    756   // Check that the foreign session was associated and retrieve the data.
    757   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    758   ASSERT_EQ(1U, foreign_sessions.size());
    759   std::vector<std::vector<SessionID::id_type> > session_reference;
    760   session_reference.push_back(tab_list1);
    761   helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    762 
    763   ASSERT_FALSE(notified_of_update_);
    764   // Now delete the foreign session.
    765   model_associator_->DeleteForeignSession(tag);
    766   ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    767 
    768   // Verify that deleteForeignSession triggers the
    769   // NOTIFICATION_FOREIGN_SESSION_DISABLED notification.
    770   ASSERT_TRUE(notified_of_update_);
    771 }
    772 
    773 // Associate both a non-stale foreign session and a stale foreign session.
    774 // Ensure only the stale session gets deleted.
    775 TEST_F(ProfileSyncServiceSessionTest, DeleteStaleSessions) {
    776   CreateRootHelper create_root(this);
    777   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    778   ASSERT_TRUE(create_root.success());
    779 
    780   // Fill two instances of session specifics with a foreign session's data.
    781   std::string tag = "tag1";
    782   SessionID::id_type nums1[] = {5, 10, 13, 17};
    783   std::vector<sync_pb::SessionSpecifics> tabs1;
    784   std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
    785   sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
    786       tag, tab_list1, &tabs1));
    787   std::string tag2 = "tag2";
    788   sync_pb::SessionSpecifics meta2;
    789   helper_.BuildSessionSpecifics(tag2, &meta2);
    790   SessionID::id_type tab_nums2[] = {8, 15, 18, 20};
    791   std::vector<SessionID::id_type> tab_list2(
    792       tab_nums2, tab_nums2 + arraysize(tab_nums2));
    793   helper_.AddWindowSpecifics(0, tab_list2, &meta2);
    794   std::vector<sync_pb::SessionSpecifics> tabs2;
    795   tabs2.resize(tab_list2.size());
    796   for (size_t i = 0; i < tab_list2.size(); ++i) {
    797     helper_.BuildTabSpecifics(tag2, 0, tab_list2[i], &tabs2[i]);
    798   }
    799 
    800   // Set the modification time for tag1 to be 21 days ago, tag2 to 5 days ago.
    801   base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21);
    802   base::Time tag2_time = base::Time::Now() - base::TimeDelta::FromDays(5);
    803 
    804   // Associate specifics.
    805   model_associator_->AssociateForeignSpecifics(meta, tag1_time);
    806   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
    807        iter != tabs1.end(); ++iter) {
    808     model_associator_->AssociateForeignSpecifics(*iter, tag1_time);
    809   }
    810   model_associator_->AssociateForeignSpecifics(meta2, tag2_time);
    811   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs2.begin();
    812        iter != tabs2.end(); ++iter) {
    813     model_associator_->AssociateForeignSpecifics(*iter, tag2_time);
    814   }
    815 
    816   // Check that the foreign session was associated and retrieve the data.
    817   std::vector<const SyncedSession*> foreign_sessions;
    818   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    819   ASSERT_EQ(2U, foreign_sessions.size());
    820 
    821   // Now delete the stale session and verify the non-stale one is still there.
    822   model_associator_->DeleteStaleSessions();
    823   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    824   ASSERT_EQ(1U, foreign_sessions.size());
    825   std::vector<std::vector<SessionID::id_type> > session_reference;
    826   session_reference.push_back(tab_list2);
    827   helper_.VerifySyncedSession(tag2, session_reference, *(foreign_sessions[0]));
    828 }
    829 
    830 // Write a stale foreign session to a node. Then update one of its tabs so
    831 // the session is no longer stale. Ensure it doesn't get deleted.
    832 TEST_F(ProfileSyncServiceSessionTest, StaleSessionRefresh) {
    833   CreateRootHelper create_root(this);
    834   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    835   ASSERT_TRUE(create_root.success());
    836 
    837   std::string tag = "tag1";
    838   SessionID::id_type nums1[] = {5, 10, 13, 17};
    839   std::vector<sync_pb::SessionSpecifics> tabs1;
    840   std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
    841   sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
    842       tag, tab_list1, &tabs1));
    843 
    844   // Associate.
    845   base::Time stale_time = base::Time::Now() - base::TimeDelta::FromDays(21);
    846   model_associator_->AssociateForeignSpecifics(meta, stale_time);
    847   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
    848        iter != tabs1.end(); ++iter) {
    849     model_associator_->AssociateForeignSpecifics(*iter, stale_time);
    850   }
    851 
    852   // Associate one of the tabs with a non-stale time.
    853   model_associator_->AssociateForeignSpecifics(tabs1[0], base::Time::Now());
    854 
    855   // Check that the foreign session was associated and retrieve the data.
    856   std::vector<const SyncedSession*> foreign_sessions;
    857   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    858   ASSERT_EQ(1U, foreign_sessions.size());
    859 
    860   // Verify the now non-stale session does not get deleted.
    861   model_associator_->DeleteStaleSessions();
    862   ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    863   ASSERT_EQ(1U, foreign_sessions.size());
    864   std::vector<std::vector<SessionID::id_type> > session_reference;
    865   session_reference.push_back(tab_list1);
    866   helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    867 }
    868 
    869 // Crashes sometimes on Windows, particularly XP.
    870 // See http://crbug.com/174951
    871 #if defined(OS_WIN)
    872 #define MAYBE_ValidTabs DISABLED_ValidTabs
    873 #else
    874 #define MAYBE_ValidTabs ValidTabs
    875 #endif  // defined(OS_WIN)
    876 
    877 // Test that tabs with nothing but "chrome://*" and "file://*" navigations are
    878 // not be synced.
    879 TEST_F(ProfileSyncServiceSessionTest, MAYBE_ValidTabs) {
    880   CreateRootHelper create_root(this);
    881   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    882   ASSERT_TRUE(create_root.success());
    883 
    884   AddTab(browser(), GURL("chrome://bla1/"));
    885   NavigateAndCommitActiveTab(GURL("chrome://bla2"));
    886   AddTab(browser(), GURL("file://bla3/"));
    887   AddTab(browser(), GURL("bla://bla"));
    888   // Note: chrome://newtab has special handling which crashes in unit tests.
    889 
    890   // Get the tabs for this machine. Only the bla:// url should be synced.
    891   SessionModelAssociator::TabLinksMap tab_map =
    892       model_associator_->local_tab_map_;
    893   ASSERT_EQ(1U, tab_map.size());
    894   SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin();
    895   ASSERT_EQ(1, iter->second->tab()->GetEntryCount());
    896   ASSERT_EQ(GURL("bla://bla"), iter->second->tab()->
    897       GetEntryAtIndex(0)->GetVirtualURL());
    898 }
    899 
    900 // Verify that AttemptSessionsDataRefresh triggers the
    901 // NOTIFICATION_SYNC_REFRESH_LOCAL notification.
    902 // TODO(zea): Once we can have unit tests that are able to open to the NTP,
    903 // test that the NTP/#opentabs URL triggers a refresh as well (but only when
    904 // it is the active tab).
    905 TEST_F(ProfileSyncServiceSessionTest, SessionsRefresh) {
    906   CreateRootHelper create_root(this);
    907   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    908   ASSERT_TRUE(create_root.success());
    909 
    910   // Empty, so returns false.
    911   std::vector<const SyncedSession*> foreign_sessions;
    912   ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    913   ASSERT_FALSE(notified_of_refresh_);
    914   model_associator_->AttemptSessionsDataRefresh();
    915   ASSERT_TRUE(notified_of_refresh_);
    916 
    917   // Nothing should have changed since we don't have unapplied data.
    918   ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    919 }
    920 
    921 // Ensure model association associates the pre-existing tabs.
    922 TEST_F(ProfileSyncServiceSessionTest, ExistingTabs) {
    923   AddTab(browser(), GURL("http://foo1"));
    924   NavigateAndCommitActiveTab(GURL("http://foo2"));
    925   AddTab(browser(), GURL("http://bar1"));
    926   NavigateAndCommitActiveTab(GURL("http://bar2"));
    927 
    928   CreateRootHelper create_root(this);
    929   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    930   ASSERT_TRUE(create_root.success());
    931   bool has_nodes;
    932   ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
    933   ASSERT_TRUE(has_nodes);
    934 
    935   std::string machine_tag = model_associator_->GetCurrentMachineTag();
    936   int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
    937   ASSERT_NE(syncer::kInvalidId, sync_id);
    938 
    939   // Check that this machine's data is not included in the foreign windows.
    940   std::vector<const SyncedSession*> foreign_sessions;
    941   ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
    942   ASSERT_EQ(foreign_sessions.size(), 0U);
    943 
    944   // Get the tabs for this machine from the node and check that they were
    945   // filled.
    946   SessionModelAssociator::TabLinksMap tab_map =
    947       model_associator_->local_tab_map_;
    948   ASSERT_EQ(2U, tab_map.size());
    949   // Tabs are ordered by sessionid in tab_map, so should be able to traverse
    950   // the tree based on order of tabs created
    951   SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin();
    952   ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
    953   ASSERT_EQ(GURL("http://foo1"), iter->second->tab()->
    954           GetEntryAtIndex(0)->GetVirtualURL());
    955   ASSERT_EQ(GURL("http://foo2"), iter->second->tab()->
    956           GetEntryAtIndex(1)->GetVirtualURL());
    957   iter++;
    958   ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
    959   ASSERT_EQ(GURL("http://bar1"), iter->second->tab()->
    960       GetEntryAtIndex(0)->GetVirtualURL());
    961   ASSERT_EQ(GURL("http://bar2"), iter->second->tab()->
    962       GetEntryAtIndex(1)->GetVirtualURL());
    963 }
    964 
    965 TEST_F(ProfileSyncServiceSessionTest, MissingHeaderAndTab) {
    966   AddTab(browser(), GURL("http://foo1"));
    967   NavigateAndCommitActiveTab(GURL("http://foo2"));
    968   AddTab(browser(), GURL("http://bar1"));
    969   NavigateAndCommitActiveTab(GURL("http://bar2"));
    970   CreateRootHelper create_root(this);
    971   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
    972   syncer::SyncError error;
    973   std::string local_tag = model_associator_->GetCurrentMachineTag();
    974 
    975   error = model_associator_->DisassociateModels();
    976   ASSERT_FALSE(error.IsSet());
    977   {
    978     // Create a sync node with the local tag but neither header nor tab field.
    979     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
    980     syncer::ReadNode root(&trans);
    981     root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS));
    982     syncer::WriteNode extra_header(&trans);
    983     syncer::WriteNode::InitUniqueByCreationResult result =
    984         extra_header.InitUniqueByCreation(syncer::SESSIONS, root, "new_tag");
    985     ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result);
    986     sync_pb::SessionSpecifics specifics;
    987     specifics.set_session_tag(local_tag);
    988     extra_header.SetSessionSpecifics(specifics);
    989   }
    990 
    991   error = model_associator_->AssociateModels(NULL, NULL);
    992   ASSERT_FALSE(error.IsSet());
    993 }
    994 
    995 TEST_F(ProfileSyncServiceSessionTest, MultipleHeaders) {
    996   AddTab(browser(), GURL("http://foo1"));
    997   NavigateAndCommitActiveTab(GURL("http://foo2"));
    998   AddTab(browser(), GURL("http://bar1"));
    999   NavigateAndCommitActiveTab(GURL("http://bar2"));
   1000   CreateRootHelper create_root(this);
   1001   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
   1002   syncer::SyncError error;
   1003   std::string local_tag = model_associator_->GetCurrentMachineTag();
   1004 
   1005   error = model_associator_->DisassociateModels();
   1006   ASSERT_FALSE(error.IsSet());
   1007   {
   1008     // Create another sync node with a header field and the local tag.
   1009     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
   1010     syncer::ReadNode root(&trans);
   1011     root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS));
   1012     syncer::WriteNode extra_header(&trans);
   1013     syncer::WriteNode::InitUniqueByCreationResult result =
   1014         extra_header.InitUniqueByCreation(syncer::SESSIONS,
   1015                                           root, local_tag + "_");
   1016     ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result);
   1017     sync_pb::SessionSpecifics specifics;
   1018     specifics.set_session_tag(local_tag);
   1019     specifics.mutable_header();
   1020     extra_header.SetSessionSpecifics(specifics);
   1021   }
   1022   error = model_associator_->AssociateModels(NULL, NULL);
   1023   ASSERT_FALSE(error.IsSet());
   1024 }
   1025 
   1026 TEST_F(ProfileSyncServiceSessionTest, CorruptedForeign) {
   1027   AddTab(browser(), GURL("http://foo1"));
   1028   NavigateAndCommitActiveTab(GURL("http://foo2"));
   1029   AddTab(browser(), GURL("http://bar1"));
   1030   NavigateAndCommitActiveTab(GURL("http://bar2"));
   1031   CreateRootHelper create_root(this);
   1032   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
   1033   syncer::SyncError error;
   1034 
   1035   error = model_associator_->DisassociateModels();
   1036   ASSERT_FALSE(error.IsSet());
   1037   {
   1038     // Create another sync node with neither header nor tab field and a foreign
   1039     // tag.
   1040     std::string foreign_tag = "foreign_tag";
   1041     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
   1042     syncer::ReadNode root(&trans);
   1043     root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS));
   1044     syncer::WriteNode extra_header(&trans);
   1045     syncer::WriteNode::InitUniqueByCreationResult result =
   1046         extra_header.InitUniqueByCreation(syncer::SESSIONS,
   1047                                           root, foreign_tag);
   1048     ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result);
   1049     sync_pb::SessionSpecifics specifics;
   1050     specifics.set_session_tag(foreign_tag);
   1051     extra_header.SetSessionSpecifics(specifics);
   1052   }
   1053   error = model_associator_->AssociateModels(NULL, NULL);
   1054   ASSERT_FALSE(error.IsSet());
   1055 }
   1056 
   1057 TEST_F(ProfileSyncServiceSessionTest, MissingLocalTabNode) {
   1058   AddTab(browser(), GURL("http://foo1"));
   1059   NavigateAndCommitActiveTab(GURL("http://foo2"));
   1060   AddTab(browser(), GURL("http://bar1"));
   1061   NavigateAndCommitActiveTab(GURL("http://bar2"));
   1062   CreateRootHelper create_root(this);
   1063   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
   1064   std::string local_tag = model_associator_->GetCurrentMachineTag();
   1065   syncer::SyncError error;
   1066 
   1067   error = model_associator_->DisassociateModels();
   1068   ASSERT_FALSE(error.IsSet());
   1069   {
   1070     // Delete the first sync tab node.
   1071     std::string tab_tag = TabNodePool::TabIdToTag(local_tag, 1);
   1072 
   1073     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
   1074     syncer::ReadNode root(&trans);
   1075     root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS));
   1076     syncer::WriteNode tab_node(&trans);
   1077     ASSERT_EQ(syncer::BaseNode::INIT_OK,
   1078               tab_node.InitByClientTagLookup(syncer::SESSIONS, tab_tag));
   1079     tab_node.Tombstone();
   1080   }
   1081   error = model_associator_->AssociateModels(NULL, NULL);
   1082   ASSERT_FALSE(error.IsSet());
   1083 
   1084   // Add some more tabs to ensure we don't conflict with the pre-existing tab
   1085   // node.
   1086   AddTab(browser(), GURL("http://baz1"));
   1087   AddTab(browser(), GURL("http://baz2"));
   1088 }
   1089 
   1090 TEST_F(ProfileSyncServiceSessionTest, Favicons) {
   1091     CreateRootHelper create_root(this);
   1092   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
   1093   ASSERT_TRUE(create_root.success());
   1094 
   1095   // Build a foreign session with one window and one tab.
   1096   std::string tag = "tag1";
   1097   sync_pb::SessionSpecifics meta;
   1098   helper_.BuildSessionSpecifics(tag, &meta);
   1099   std::vector<SessionID::id_type> tab_list;
   1100   tab_list.push_back(5);
   1101   helper_.AddWindowSpecifics(0, tab_list, &meta);
   1102   sync_pb::SessionSpecifics tab;
   1103   helper_.BuildTabSpecifics(tag, 0, tab_list[0], &tab);
   1104   std::string url = tab.tab().navigation(0).virtual_url();
   1105   scoped_refptr<base::RefCountedMemory> favicon;
   1106 
   1107   // Update associator.
   1108   model_associator_->AssociateForeignSpecifics(meta, base::Time());
   1109   model_associator_->AssociateForeignSpecifics(tab, base::Time());
   1110   base::RunLoop().RunUntilIdle();
   1111   ASSERT_FALSE(model_associator_->GetSyncedFaviconForPageURL(url, &favicon));
   1112 
   1113   // Now add a favicon.
   1114   tab.mutable_tab()->set_favicon_source("http://favicon_source.com/png.ico");
   1115   tab.mutable_tab()->set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
   1116   tab.mutable_tab()->set_favicon("data");
   1117   model_associator_->AssociateForeignSpecifics(tab, base::Time());
   1118   base::RunLoop().RunUntilIdle();
   1119   ASSERT_TRUE(model_associator_->GetSyncedFaviconForPageURL(url, &favicon));
   1120   ASSERT_TRUE(CompareMemoryToString("data", favicon));
   1121 
   1122   // Simulate navigating away. The associator should not delete the favicon.
   1123   tab.mutable_tab()->clear_navigation();
   1124   tab.mutable_tab()->add_navigation()->set_virtual_url("http://new_url.com");
   1125   tab.mutable_tab()->clear_favicon_source();
   1126   tab.mutable_tab()->clear_favicon_type();
   1127   tab.mutable_tab()->clear_favicon();
   1128   model_associator_->AssociateForeignSpecifics(tab, base::Time());
   1129   base::RunLoop().RunUntilIdle();
   1130   ASSERT_TRUE(model_associator_->GetSyncedFaviconForPageURL(url, &favicon));
   1131 }
   1132 
   1133 TEST_F(ProfileSyncServiceSessionTest, CorruptedLocalHeader) {
   1134   AddTab(browser(), GURL("http://foo1"));
   1135   NavigateAndCommitActiveTab(GURL("http://foo2"));
   1136   AddTab(browser(), GURL("http://bar1"));
   1137   NavigateAndCommitActiveTab(GURL("http://bar2"));
   1138   CreateRootHelper create_root(this);
   1139   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
   1140   std::string local_tag = model_associator_->GetCurrentMachineTag();
   1141   syncer::SyncError error;
   1142 
   1143   error = model_associator_->DisassociateModels();
   1144   ASSERT_FALSE(error.IsSet());
   1145   {
   1146     // Load the header node and clear it.
   1147     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
   1148     syncer::WriteNode header(&trans);
   1149     ASSERT_EQ(syncer::BaseNode::INIT_OK,
   1150               header.InitByClientTagLookup(syncer::SESSIONS, local_tag));
   1151     sync_pb::SessionSpecifics specifics;
   1152     header.SetSessionSpecifics(specifics);
   1153   }
   1154   // Ensure we associate properly despite the pre-existing node with our local
   1155   // tag.
   1156   error = model_associator_->AssociateModels(NULL, NULL);
   1157   ASSERT_FALSE(error.IsSet());
   1158 }
   1159 
   1160 TEST_F(ProfileSyncServiceSessionTest, CheckPrerenderedWebContentsSwap) {
   1161   AddTab(browser(), GURL("http://foo1"));
   1162   NavigateAndCommitActiveTab(GURL("http://foo2"));
   1163   CreateRootHelper create_root(this);
   1164   // Test setup.
   1165   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
   1166 
   1167   syncer::SyncError error;
   1168   // Initial association.
   1169   EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
   1170   ASSERT_FALSE(error.IsSet());
   1171 
   1172   // To simulate WebContents swap during prerendering, create new WebContents
   1173   // and swap with old WebContents.
   1174   content::WebContents* old_web_contents =
   1175       browser()->tab_strip_model()->GetActiveWebContents();
   1176 
   1177   // Create new WebContents, with the required tab helpers.
   1178   WebContents* new_web_contents = WebContents::CreateWithSessionStorage(
   1179       WebContents::CreateParams(profile()),
   1180       old_web_contents->GetController().GetSessionStorageNamespaceMap());
   1181   SessionTabHelper::CreateForWebContents(new_web_contents);
   1182   TabContentsSyncedTabDelegate::CreateForWebContents(new_web_contents);
   1183   new_web_contents->GetController()
   1184       .CopyStateFrom(old_web_contents->GetController());
   1185 
   1186   // Swap the WebContents.
   1187   int index =
   1188       browser()->tab_strip_model()->GetIndexOfWebContents(old_web_contents);
   1189   browser()->tab_strip_model()->ReplaceWebContentsAt(index, new_web_contents);
   1190 
   1191   EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
   1192   ASSERT_FALSE(error.IsSet());
   1193   // Navigate away.
   1194   NavigateAndCommitActiveTab(GURL("http://bar2"));
   1195   EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
   1196   ASSERT_FALSE(error.IsSet());
   1197 
   1198   // Delete old WebContents. This should not crash.
   1199   delete old_web_contents;
   1200   EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
   1201   ASSERT_FALSE(error.IsSet());
   1202 
   1203   // Try more navigations to make sure everything if fine.
   1204   NavigateAndCommitActiveTab(GURL("http://bar3"));
   1205   EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
   1206   ASSERT_FALSE(error.IsSet());
   1207 
   1208   AddTab(browser(), GURL("http://bar4"));
   1209   EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
   1210   ASSERT_FALSE(error.IsSet());
   1211   NavigateAndCommitActiveTab(GURL("http://bar5"));
   1212   EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
   1213   ASSERT_FALSE(error.IsSet());
   1214 }
   1215 
   1216 TEST_F(ProfileSyncServiceSessionTest, TabPoolFreeNodeLimits) {
   1217   CreateRootHelper create_root(this);
   1218   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
   1219   ASSERT_TRUE(create_root.success());
   1220 
   1221   // Allocate TabNodePool::kFreeNodesHighWatermark + 1 nodes and verify that
   1222   // freeing the last node reduces the free node pool size to
   1223   // kFreeNodesLowWatermark.
   1224 
   1225   SessionID session_id;
   1226   std::vector<int> used_sync_ids;
   1227   for (size_t i = 1; i <= TabNodePool::kFreeNodesHighWatermark + 1; ++i) {
   1228     session_id.set_id(i);
   1229     int sync_id = model_associator_->local_tab_pool_.GetFreeTabNode();
   1230     model_associator_->local_tab_pool_.AssociateTabNode(sync_id, i);
   1231     used_sync_ids.push_back(sync_id);
   1232   }
   1233 
   1234   // Free all except one node.
   1235   int last_sync_id = used_sync_ids.back();
   1236   used_sync_ids.pop_back();
   1237 
   1238   for (size_t i = 0; i < used_sync_ids.size(); ++i) {
   1239     model_associator_->local_tab_pool_.FreeTabNode(used_sync_ids[i]);
   1240   }
   1241 
   1242   // Except one node all nodes should be in FreeNode pool.
   1243   EXPECT_FALSE(model_associator_->local_tab_pool_.Full());
   1244   EXPECT_FALSE(model_associator_->local_tab_pool_.Empty());
   1245   // Total capacity = 1 Associated Node + kFreeNodesHighWatermark free node.
   1246   EXPECT_EQ(TabNodePool::kFreeNodesHighWatermark + 1,
   1247             model_associator_->local_tab_pool_.Capacity());
   1248 
   1249   // Freeing the last sync node should drop the free nodes to
   1250   // kFreeNodesLowWatermark.
   1251   model_associator_->local_tab_pool_.FreeTabNode(last_sync_id);
   1252   EXPECT_FALSE(model_associator_->local_tab_pool_.Empty());
   1253   EXPECT_TRUE(model_associator_->local_tab_pool_.Full());
   1254   EXPECT_EQ(TabNodePool::kFreeNodesLowWatermark,
   1255             model_associator_->local_tab_pool_.Capacity());
   1256 }
   1257 
   1258 TEST_F(ProfileSyncServiceSessionTest, TabNodePoolDeleteUnassociatedNodes) {
   1259   CreateRootHelper create_root(this);
   1260   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
   1261   std::string local_tag = model_associator_->GetCurrentMachineTag();
   1262   syncer::SyncError error;
   1263   // Create a free node and then dissassociate sessions so that it ends up
   1264   // unassociated.
   1265   int tab_node_id = model_associator_->local_tab_pool_.GetFreeTabNode();
   1266   // Update the tab_id of the node, so that it is considered a valid
   1267   // unassociated node otherwise it will be mistaken for a corrupted node and
   1268   // will be deleted before being added to the tab node pool.
   1269   {
   1270     std::string tab_tag = TabNodePool::TabIdToTag(local_tag, tab_node_id);
   1271     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
   1272     syncer::WriteNode tab_node(&trans);
   1273     ASSERT_EQ(syncer::BaseNode::INIT_OK,
   1274               tab_node.InitByClientTagLookup(syncer::SESSIONS, tab_tag));
   1275     sync_pb::SessionSpecifics specifics = tab_node.GetSessionSpecifics();
   1276     sync_pb::SessionTab* tab = specifics.mutable_tab();
   1277     tab->set_tab_id(1);
   1278     tab_node.SetSessionSpecifics(specifics);
   1279   }
   1280 
   1281   error = model_associator_->DisassociateModels();
   1282   ASSERT_FALSE(error.IsSet());
   1283   error = model_associator_->AssociateModels(NULL, NULL);
   1284   ASSERT_FALSE(error.IsSet());
   1285 }
   1286 
   1287 }  // namespace browser_sync
   1288