profile_sync_service_session_unittest.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1// Copyright (c) 2010 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/message_loop.h" 9#include "base/scoped_ptr.h" 10#include "base/scoped_temp_dir.h" 11#include "base/stl_util-inl.h" 12#include "base/task.h" 13#include "chrome/browser/sessions/session_service.h" 14#include "chrome/browser/sessions/session_service_test_helper.h" 15#include "chrome/browser/sync/abstract_profile_sync_service_test.h" 16#include "chrome/browser/sync/engine/syncapi.h" 17#include "chrome/browser/sync/glue/session_change_processor.h" 18#include "chrome/browser/sync/glue/session_data_type_controller.h" 19#include "chrome/browser/sync/glue/session_model_associator.h" 20#include "chrome/browser/sync/glue/sync_backend_host.h" 21#include "chrome/browser/sync/profile_sync_test_util.h" 22#include "chrome/browser/sync/profile_sync_factory_mock.h" 23#include "chrome/browser/sync/protocol/session_specifics.pb.h" 24#include "chrome/browser/sync/protocol/sync.pb.h" 25#include "chrome/browser/sync/syncable/directory_manager.h" 26#include "chrome/browser/sync/syncable/model_type.h" 27#include "chrome/browser/sync/syncable/syncable.h" 28#include "chrome/browser/sync/test_profile_sync_service.h" 29#include "chrome/common/net/gaia/gaia_constants.h" 30#include "chrome/common/notification_observer.h" 31#include "chrome/common/notification_registrar.h" 32#include "chrome/common/notification_service.h" 33#include "chrome/test/browser_with_test_window_test.h" 34#include "chrome/test/file_test_utils.h" 35#include "chrome/test/profile_mock.h" 36#include "chrome/test/testing_profile.h" 37#include "chrome/test/sync/engine/test_id_factory.h" 38#include "testing/gmock/include/gmock/gmock.h" 39#include "testing/gtest/include/gtest/gtest.h" 40 41using browser_sync::SessionChangeProcessor; 42using browser_sync::SessionDataTypeController; 43using browser_sync::SessionModelAssociator; 44using browser_sync::SyncBackendHost; 45using sync_api::SyncManager; 46using testing::_; 47using testing::Return; 48using browser_sync::TestIdFactory; 49 50namespace browser_sync { 51 52class ProfileSyncServiceSessionTest 53 : public BrowserWithTestWindowTest, 54 public NotificationObserver { 55 public: 56 ProfileSyncServiceSessionTest() 57 : window_bounds_(0, 1, 2, 3), 58 notified_of_update_(false) {} 59 ProfileSyncService* sync_service() { return sync_service_.get(); } 60 61 TestIdFactory* ids() { return sync_service_->id_factory(); } 62 63 protected: 64 SessionService* service() { return helper_.service(); } 65 66 virtual void SetUp() { 67 // BrowserWithTestWindowTest implementation. 68 BrowserWithTestWindowTest::SetUp(); 69 70 profile()->set_has_history_service(true); 71 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 72 SessionService* session_service = new SessionService(temp_dir_.path()); 73 helper_.set_service(session_service); 74 service()->SetWindowType(window_id_, Browser::TYPE_NORMAL); 75 service()->SetWindowBounds(window_id_, window_bounds_, false); 76 registrar_.Add(this, NotificationType::FOREIGN_SESSION_UPDATED, 77 NotificationService::AllSources()); 78 } 79 80 void Observe(NotificationType type, 81 const NotificationSource& source, 82 const NotificationDetails& details) { 83 switch (type.value) { 84 case NotificationType::FOREIGN_SESSION_UPDATED: 85 notified_of_update_ = true; 86 break; 87 default: 88 NOTREACHED(); 89 break; 90 } 91 } 92 93 virtual void TearDown() { 94 helper_.set_service(NULL); 95 profile()->set_session_service(NULL); 96 sync_service_.reset(); 97 } 98 99 bool StartSyncService(Task* task, bool will_fail_association) { 100 if (sync_service_.get()) 101 return false; 102 sync_service_.reset(new TestProfileSyncService( 103 &factory_, profile(), "test user", false, task)); 104 profile()->set_session_service(helper_.service()); 105 106 // Register the session data type. 107 model_associator_ = 108 new SessionModelAssociator(sync_service_.get(), 109 true /* setup_for_test */); 110 change_processor_ = new SessionChangeProcessor( 111 sync_service_.get(), model_associator_, 112 true /* setup_for_test */); 113 EXPECT_CALL(factory_, CreateSessionSyncComponents(_, _)). 114 WillOnce(Return(ProfileSyncFactory::SyncComponents( 115 model_associator_, change_processor_))); 116 EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). 117 WillOnce(ReturnNewDataTypeManager()); 118 sync_service_->set_num_expected_resumes(will_fail_association ? 0 : 1); 119 sync_service_->RegisterDataTypeController( 120 new SessionDataTypeController(&factory_, sync_service_.get())); 121 profile()->GetTokenService()->IssueAuthTokenForTest( 122 GaiaConstants::kSyncService, "token"); 123 sync_service_->Initialize(); 124 MessageLoop::current()->Run(); 125 return true; 126 } 127 128 // Path used in testing. 129 ScopedTempDir temp_dir_; 130 SessionServiceTestHelper helper_; 131 SessionModelAssociator* model_associator_; 132 SessionChangeProcessor* change_processor_; 133 SessionID window_id_; 134 ProfileSyncFactoryMock factory_; 135 scoped_ptr<TestProfileSyncService> sync_service_; 136 const gfx::Rect window_bounds_; 137 bool notified_of_update_; 138 NotificationRegistrar registrar_; 139}; 140 141class CreateRootTask : public Task { 142 public: 143 explicit CreateRootTask(ProfileSyncServiceSessionTest* test) 144 : test_(test), success_(false) { 145 } 146 147 virtual ~CreateRootTask() {} 148 virtual void Run() { 149 success_ = ProfileSyncServiceTestHelper::CreateRoot( 150 syncable::SESSIONS, 151 test_->sync_service()->GetUserShare(), 152 test_->ids()); 153 } 154 155 bool success() { return success_; } 156 157 private: 158 ProfileSyncServiceSessionTest* test_; 159 bool success_; 160}; 161 162// Test that we can write this machine's session to a node and retrieve it. 163TEST_F(ProfileSyncServiceSessionTest, WriteSessionToNode) { 164 CreateRootTask task(this); 165 ASSERT_TRUE(StartSyncService(&task, false)); 166 ASSERT_TRUE(task.success()); 167 ASSERT_EQ(model_associator_->GetSessionService(), helper_.service()); 168 169 // Check that the DataTypeController associated the models. 170 bool has_nodes; 171 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes)); 172 ASSERT_TRUE(has_nodes); 173 std::string machine_tag = model_associator_->GetCurrentMachineTag(); 174 int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag); 175 ASSERT_NE(sync_api::kInvalidId, sync_id); 176 177 // Check that we can get the correct session specifics back from the node. 178 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); 179 sync_api::ReadNode node(&trans); 180 ASSERT_TRUE(node.InitByClientTagLookup(syncable::SESSIONS, 181 machine_tag)); 182 const sync_pb::SessionSpecifics& specifics(node.GetSessionSpecifics()); 183 ASSERT_EQ(machine_tag, specifics.session_tag()); 184 ASSERT_TRUE(specifics.has_header()); 185 const sync_pb::SessionHeader& header_s = specifics.header(); 186 ASSERT_EQ(0, header_s.window_size()); 187} 188 189// Test that we can fill this machine's session, write it to a node, 190// and then retrieve it. 191TEST_F(ProfileSyncServiceSessionTest, WriteFilledSessionToNode) { 192 CreateRootTask task(this); 193 ASSERT_TRUE(StartSyncService(&task, false)); 194 ASSERT_TRUE(task.success()); 195 196 // Check that the DataTypeController associated the models. 197 bool has_nodes; 198 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes)); 199 ASSERT_TRUE(has_nodes); 200 AddTab(browser(), GURL("http://foo/1")); 201 NavigateAndCommitActiveTab(GURL("http://foo/2")); 202 AddTab(browser(), GURL("http://bar/1")); 203 NavigateAndCommitActiveTab(GURL("http://bar/2")); 204 205 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes)); 206 ASSERT_TRUE(has_nodes); 207 std::string machine_tag = model_associator_->GetCurrentMachineTag(); 208 int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag); 209 ASSERT_NE(sync_api::kInvalidId, sync_id); 210 211 // Check that this machine's data is not included in the foreign windows. 212 std::vector<const ForeignSession*> foreign_sessions; 213 model_associator_->GetAllForeignSessions(&foreign_sessions); 214 ASSERT_EQ(foreign_sessions.size(), 0U); 215 216 // Get the tabs for this machine from the node and check that they were 217 // filled. 218 SessionModelAssociator::TabLinksMap tab_map = model_associator_->tab_map_; 219 ASSERT_EQ(2U, tab_map.size()); 220 // Tabs are ordered by sessionid in tab_map, so should be able to traverse 221 // the tree based on order of tabs created 222 SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin(); 223 ASSERT_EQ(2, iter->second.tab()->controller().entry_count()); 224 ASSERT_EQ(GURL("http://foo/1"), iter->second.tab()->controller(). 225 GetEntryAtIndex(0)->virtual_url()); 226 ASSERT_EQ(GURL("http://foo/2"), iter->second.tab()->controller(). 227 GetEntryAtIndex(1)->virtual_url()); 228 iter++; 229 ASSERT_EQ(2, iter->second.tab()->controller().entry_count()); 230 ASSERT_EQ(GURL("http://bar/1"), iter->second.tab()->controller(). 231 GetEntryAtIndex(0)->virtual_url()); 232 ASSERT_EQ(GURL("http://bar/2"), iter->second.tab()->controller(). 233 GetEntryAtIndex(1)->virtual_url()); 234} 235 236// Test that we fail on a failed model association. 237TEST_F(ProfileSyncServiceSessionTest, FailModelAssociation) { 238 ASSERT_TRUE(StartSyncService(NULL, true)); 239 ASSERT_TRUE(sync_service_->unrecoverable_error_detected()); 240} 241 242// Write a foreign session to a node, and then retrieve it. 243TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) { 244 CreateRootTask task(this); 245 ASSERT_TRUE(StartSyncService(&task, false)); 246 ASSERT_TRUE(task.success()); 247 248 // Check that the DataTypeController associated the models. 249 bool has_nodes; 250 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes)); 251 ASSERT_TRUE(has_nodes); 252 253 // Fill an instance of session specifics with a foreign session's data. 254 sync_pb::SessionSpecifics meta_specifics; 255 std::string machine_tag = "session_sync123"; 256 meta_specifics.set_session_tag(machine_tag); 257 sync_pb::SessionHeader* header_s = meta_specifics.mutable_header(); 258 sync_pb::SessionWindow* window_s = header_s->add_window(); 259 window_s->add_tab(0); 260 window_s->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_NORMAL); 261 window_s->set_selected_tab_index(1); 262 263 sync_pb::SessionSpecifics tab_specifics; 264 tab_specifics.set_session_tag(machine_tag); 265 sync_pb::SessionTab* tab = tab_specifics.mutable_tab(); 266 tab->set_tab_visual_index(13); 267 tab->set_current_navigation_index(3); 268 tab->set_pinned(true); 269 tab->set_extension_app_id("app_id"); 270 sync_pb::TabNavigation* navigation = tab->add_navigation(); 271 navigation->set_index(12); 272 navigation->set_virtual_url("http://foo/1"); 273 navigation->set_referrer("referrer"); 274 navigation->set_title("title"); 275 navigation->set_page_transition(sync_pb::TabNavigation_PageTransition_TYPED); 276 277 // Update the server with the session specifics. 278 { 279 model_associator_->AssociateForeignSpecifics(meta_specifics, 0); 280 model_associator_->AssociateForeignSpecifics(tab_specifics, 0); 281 } 282 283 // Check that the foreign session was associated and retrieve the data. 284 std::vector<const ForeignSession*> foreign_sessions; 285 model_associator_->GetAllForeignSessions(&foreign_sessions); 286 ASSERT_EQ(1U, foreign_sessions.size()); 287 ASSERT_EQ(machine_tag, foreign_sessions[0]->foreign_session_tag); 288 ASSERT_EQ(1U, foreign_sessions[0]->windows.size()); 289 ASSERT_EQ(1U, foreign_sessions[0]->windows[0]->tabs.size()); 290 ASSERT_EQ(1U, foreign_sessions[0]->windows[0]->tabs[0]->navigations.size()); 291 ASSERT_EQ(foreign_sessions[0]->foreign_session_tag, machine_tag); 292 ASSERT_EQ(1, foreign_sessions[0]->windows[0]->selected_tab_index); 293 ASSERT_EQ(1, foreign_sessions[0]->windows[0]->type); 294 ASSERT_EQ(13, foreign_sessions[0]->windows[0]->tabs[0]->tab_visual_index); 295 ASSERT_EQ(3, 296 foreign_sessions[0]->windows[0]->tabs[0]->current_navigation_index); 297 ASSERT_TRUE(foreign_sessions[0]->windows[0]->tabs[0]->pinned); 298 ASSERT_EQ("app_id", 299 foreign_sessions[0]->windows[0]->tabs[0]->extension_app_id); 300 ASSERT_EQ(12, 301 foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].index()); 302 ASSERT_EQ(GURL("referrer"), 303 foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].referrer()); 304 ASSERT_EQ(string16(ASCIIToUTF16("title")), 305 foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].title()); 306 ASSERT_EQ(PageTransition::TYPED, 307 foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].transition()); 308 ASSERT_EQ(GURL("http://foo/1"), 309 foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].virtual_url()); 310} 311 312// Test the DataTypeController on update. 313TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionUpdate) { 314 CreateRootTask task(this); 315 ASSERT_TRUE(StartSyncService(&task, false)); 316 ASSERT_TRUE(task.success()); 317 int64 node_id = model_associator_->GetSyncIdFromSessionTag( 318 model_associator_->GetCurrentMachineTag()); 319 scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord); 320 record->action = SyncManager::ChangeRecord::ACTION_UPDATE; 321 record->id = node_id; 322 ASSERT_FALSE(notified_of_update_); 323 { 324 sync_api::WriteTransaction trans(sync_service_->GetUserShare()); 325 change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1); 326 } 327 ASSERT_TRUE(notified_of_update_); 328} 329 330// Test the DataTypeController on add. 331TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionAdd) { 332 CreateRootTask task(this); 333 ASSERT_TRUE(StartSyncService(&task, false)); 334 ASSERT_TRUE(task.success()); 335 336 int64 node_id = model_associator_->GetSyncIdFromSessionTag( 337 model_associator_->GetCurrentMachineTag()); 338 scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord); 339 record->action = SyncManager::ChangeRecord::ACTION_ADD; 340 record->id = node_id; 341 ASSERT_FALSE(notified_of_update_); 342 { 343 sync_api::WriteTransaction trans(sync_service_->GetUserShare()); 344 change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1); 345 } 346 ASSERT_TRUE(notified_of_update_); 347} 348 349// Test the DataTypeController on delete. 350TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionDelete) { 351 CreateRootTask task(this); 352 ASSERT_TRUE(StartSyncService(&task, false)); 353 ASSERT_TRUE(task.success()); 354 355 int64 node_id = model_associator_->GetSyncIdFromSessionTag( 356 model_associator_->GetCurrentMachineTag()); 357 scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord); 358 record->action = SyncManager::ChangeRecord::ACTION_DELETE; 359 record->id = node_id; 360 ASSERT_FALSE(notified_of_update_); 361 { 362 sync_api::WriteTransaction trans(sync_service_->GetUserShare()); 363 change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1); 364 } 365 ASSERT_TRUE(notified_of_update_); 366} 367// Test the TabNodePool when it starts off empty. 368TEST_F(ProfileSyncServiceSessionTest, TabNodePoolEmpty) { 369 CreateRootTask task(this); 370 ASSERT_TRUE(StartSyncService(&task, false)); 371 ASSERT_TRUE(task.success()); 372 373 std::vector<int64> node_ids; 374 ASSERT_EQ(0U, model_associator_->tab_pool_.capacity()); 375 ASSERT_TRUE(model_associator_->tab_pool_.empty()); 376 ASSERT_TRUE(model_associator_->tab_pool_.full()); 377 const size_t num_ids = 10; 378 for (size_t i = 0; i < num_ids; ++i) { 379 int64 id = model_associator_->tab_pool_.GetFreeTabNode(); 380 ASSERT_GT(id, -1); 381 node_ids.push_back(id); 382 } 383 ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity()); 384 ASSERT_TRUE(model_associator_->tab_pool_.empty()); 385 ASSERT_FALSE(model_associator_->tab_pool_.full()); 386 for (size_t i = 0; i < num_ids; ++i) { 387 model_associator_->tab_pool_.FreeTabNode(node_ids[i]); 388 } 389 ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity()); 390 ASSERT_FALSE(model_associator_->tab_pool_.empty()); 391 ASSERT_TRUE(model_associator_->tab_pool_.full()); 392} 393 394// Test the TabNodePool when it starts off with nodes 395TEST_F(ProfileSyncServiceSessionTest, TabNodePoolNonEmpty) { 396 CreateRootTask task(this); 397 ASSERT_TRUE(StartSyncService(&task, false)); 398 ASSERT_TRUE(task.success()); 399 400 const size_t num_starting_nodes = 3; 401 for (size_t i = 0; i < num_starting_nodes; ++i) { 402 model_associator_->tab_pool_.AddTabNode(i); 403 } 404 405 std::vector<int64> node_ids; 406 ASSERT_EQ(num_starting_nodes, model_associator_->tab_pool_.capacity()); 407 ASSERT_FALSE(model_associator_->tab_pool_.empty()); 408 ASSERT_TRUE(model_associator_->tab_pool_.full()); 409 const size_t num_ids = 10; 410 for (size_t i = 0; i < num_ids; ++i) { 411 int64 id = model_associator_->tab_pool_.GetFreeTabNode(); 412 ASSERT_GT(id, -1); 413 node_ids.push_back(id); 414 } 415 ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity()); 416 ASSERT_TRUE(model_associator_->tab_pool_.empty()); 417 ASSERT_FALSE(model_associator_->tab_pool_.full()); 418 for (size_t i = 0; i < num_ids; ++i) { 419 model_associator_->tab_pool_.FreeTabNode(node_ids[i]); 420 } 421 ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity()); 422 ASSERT_FALSE(model_associator_->tab_pool_.empty()); 423 ASSERT_TRUE(model_associator_->tab_pool_.full()); 424} 425 426} // namespace browser_sync 427