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