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 "base/callback.h" 6#include "base/message_loop/message_loop.h" 7#include "chrome/browser/sync/glue/fake_data_type_controller.h" 8#include "chrome/browser/sync/glue/model_association_manager.h" 9#include "content/public/test/test_browser_thread.h" 10#include "testing/gmock/include/gmock/gmock.h" 11#include "testing/gtest/include/gtest/gtest.h" 12 13using ::testing::_; 14namespace browser_sync { 15class MockModelAssociationResultProcessor : 16 public ModelAssociationResultProcessor { 17 public: 18 MockModelAssociationResultProcessor() {} 19 ~MockModelAssociationResultProcessor() {} 20 MOCK_METHOD2(OnSingleDataTypeAssociationDone, 21 void(syncer::ModelType type, 22 const syncer::DataTypeAssociationStats& association_stats)); 23 MOCK_METHOD1(OnModelAssociationDone, void( 24 const DataTypeManager::ConfigureResult& result)); 25 MOCK_METHOD0(OnTypesLoaded, void()); 26}; 27 28FakeDataTypeController* GetController( 29 const DataTypeController::TypeMap& controllers, 30 syncer::ModelType model_type) { 31 DataTypeController::TypeMap::const_iterator it = 32 controllers.find(model_type); 33 if (it == controllers.end()) { 34 return NULL; 35 } 36 return (FakeDataTypeController*)(it->second.get()); 37} 38 39ACTION_P(VerifyResult, expected_result) { 40 EXPECT_EQ(arg0.status, expected_result.status); 41 EXPECT_TRUE(arg0.requested_types.Equals(expected_result.requested_types)); 42 EXPECT_EQ(arg0.failed_data_types.size(), 43 expected_result.failed_data_types.size()); 44 45 if (arg0.failed_data_types.size() == 46 expected_result.failed_data_types.size()) { 47 std::map<syncer::ModelType, syncer::SyncError>::const_iterator it1, it2; 48 for (it1 = arg0.failed_data_types.begin(), 49 it2 = expected_result.failed_data_types.begin(); 50 it1 != arg0.failed_data_types.end(); 51 ++it1, ++it2) { 52 EXPECT_EQ((*it1).first, (*it2).first); 53 } 54 } 55 56 EXPECT_TRUE(arg0.waiting_to_start.Equals(expected_result.waiting_to_start)); 57} 58 59class SyncModelAssociationManagerTest : public testing::Test { 60 public: 61 SyncModelAssociationManagerTest() : 62 ui_thread_(content::BrowserThread::UI, &ui_loop_) { 63 } 64 65 protected: 66 base::MessageLoopForUI ui_loop_; 67 content::TestBrowserThread ui_thread_; 68 MockModelAssociationResultProcessor result_processor_; 69 DataTypeController::TypeMap controllers_; 70}; 71 72// Start a type and make sure ModelAssociationManager callst the |Start| 73// method and calls the callback when it is done. 74TEST_F(SyncModelAssociationManagerTest, SimpleModelStart) { 75 controllers_[syncer::BOOKMARKS] = 76 new FakeDataTypeController(syncer::BOOKMARKS); 77 ModelAssociationManager model_association_manager(&controllers_, 78 &result_processor_); 79 syncer::ModelTypeSet types; 80 types.Put(syncer::BOOKMARKS); 81 DataTypeManager::ConfigureResult expected_result( 82 DataTypeManager::OK, 83 types, 84 std::map<syncer::ModelType, syncer::SyncError>(), 85 syncer::ModelTypeSet(), 86 syncer::ModelTypeSet()); 87 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). 88 WillOnce(VerifyResult(expected_result)); 89 90 model_association_manager.Initialize(types); 91 model_association_manager.StopDisabledTypes(); 92 model_association_manager.StartAssociationAsync(types); 93 94 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 95 DataTypeController::ASSOCIATING); 96 GetController(controllers_, syncer::BOOKMARKS)->FinishStart( 97 DataTypeController::OK); 98} 99 100// Start a type and call stop before it finishes associating. 101TEST_F(SyncModelAssociationManagerTest, StopModelBeforeFinish) { 102 controllers_[syncer::BOOKMARKS] = 103 new FakeDataTypeController(syncer::BOOKMARKS); 104 ModelAssociationManager model_association_manager( 105 &controllers_, 106 &result_processor_); 107 108 syncer::ModelTypeSet types; 109 types.Put(syncer::BOOKMARKS); 110 111 DataTypeManager::ConfigureResult expected_result( 112 DataTypeManager::ABORTED, 113 types, 114 std::map<syncer::ModelType, syncer::SyncError>(), 115 syncer::ModelTypeSet(), 116 syncer::ModelTypeSet()); 117 118 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). 119 WillOnce(VerifyResult(expected_result)); 120 121 model_association_manager.Initialize(types); 122 model_association_manager.StopDisabledTypes(); 123 model_association_manager.StartAssociationAsync(types); 124 125 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 126 DataTypeController::ASSOCIATING); 127 model_association_manager.Stop(); 128 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 129 DataTypeController::NOT_RUNNING); 130} 131 132// Start a type, let it finish and then call stop. 133TEST_F(SyncModelAssociationManagerTest, StopAfterFinish) { 134 controllers_[syncer::BOOKMARKS] = 135 new FakeDataTypeController(syncer::BOOKMARKS); 136 ModelAssociationManager model_association_manager( 137 &controllers_, 138 &result_processor_); 139 syncer::ModelTypeSet types; 140 types.Put(syncer::BOOKMARKS); 141 DataTypeManager::ConfigureResult expected_result( 142 DataTypeManager::OK, 143 types, 144 std::map<syncer::ModelType, syncer::SyncError>(), 145 syncer::ModelTypeSet(), 146 syncer::ModelTypeSet()); 147 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). 148 WillOnce(VerifyResult(expected_result)); 149 150 model_association_manager.Initialize(types); 151 model_association_manager.StopDisabledTypes(); 152 model_association_manager.StartAssociationAsync(types); 153 154 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 155 DataTypeController::ASSOCIATING); 156 GetController(controllers_, syncer::BOOKMARKS)->FinishStart( 157 DataTypeController::OK); 158 159 model_association_manager.Stop(); 160 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 161 DataTypeController::NOT_RUNNING); 162} 163 164// Make a type fail model association and verify correctness. 165TEST_F(SyncModelAssociationManagerTest, TypeFailModelAssociation) { 166 controllers_[syncer::BOOKMARKS] = 167 new FakeDataTypeController(syncer::BOOKMARKS); 168 ModelAssociationManager model_association_manager( 169 &controllers_, 170 &result_processor_); 171 syncer::ModelTypeSet types; 172 types.Put(syncer::BOOKMARKS); 173 std::map<syncer::ModelType, syncer::SyncError> errors; 174 syncer::SyncError error(FROM_HERE, 175 syncer::SyncError::DATATYPE_ERROR, 176 "Failed", 177 syncer::BOOKMARKS); 178 errors[syncer::BOOKMARKS] = error; 179 DataTypeManager::ConfigureResult expected_result( 180 DataTypeManager::PARTIAL_SUCCESS, 181 types, 182 errors, 183 syncer::ModelTypeSet(), 184 syncer::ModelTypeSet()); 185 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). 186 WillOnce(VerifyResult(expected_result)); 187 188 model_association_manager.Initialize(types); 189 model_association_manager.StopDisabledTypes(); 190 model_association_manager.StartAssociationAsync(types); 191 192 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 193 DataTypeController::ASSOCIATING); 194 GetController(controllers_, syncer::BOOKMARKS)->FinishStart( 195 DataTypeController::ASSOCIATION_FAILED); 196 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 197 DataTypeController::NOT_RUNNING); 198} 199 200// Ensure configuring stops when a type returns a unrecoverable error. 201TEST_F(SyncModelAssociationManagerTest, TypeReturnUnrecoverableError) { 202 controllers_[syncer::BOOKMARKS] = 203 new FakeDataTypeController(syncer::BOOKMARKS); 204 ModelAssociationManager model_association_manager( 205 &controllers_, 206 &result_processor_); 207 syncer::ModelTypeSet types; 208 types.Put(syncer::BOOKMARKS); 209 std::map<syncer::ModelType, syncer::SyncError> errors; 210 syncer::SyncError error(FROM_HERE, 211 syncer::SyncError::DATATYPE_ERROR, 212 "Failed", 213 syncer::BOOKMARKS); 214 errors[syncer::BOOKMARKS] = error; 215 DataTypeManager::ConfigureResult expected_result( 216 DataTypeManager::UNRECOVERABLE_ERROR, 217 types, 218 errors, 219 syncer::ModelTypeSet(), 220 syncer::ModelTypeSet()); 221 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). 222 WillOnce(VerifyResult(expected_result)); 223 224 model_association_manager.Initialize(types); 225 model_association_manager.StopDisabledTypes(); 226 model_association_manager.StartAssociationAsync(types); 227 228 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 229 DataTypeController::ASSOCIATING); 230 GetController(controllers_, syncer::BOOKMARKS)->FinishStart( 231 DataTypeController::UNRECOVERABLE_ERROR); 232} 233 234TEST_F(SyncModelAssociationManagerTest, InitializeAbortsLoad) { 235 controllers_[syncer::BOOKMARKS] = 236 new FakeDataTypeController(syncer::BOOKMARKS); 237 controllers_[syncer::THEMES] = 238 new FakeDataTypeController(syncer::THEMES); 239 240 GetController(controllers_, syncer::BOOKMARKS)->SetDelayModelLoad(); 241 ModelAssociationManager model_association_manager(&controllers_, 242 &result_processor_); 243 syncer::ModelTypeSet types(syncer::BOOKMARKS, syncer::THEMES); 244 245 syncer::ModelTypeSet expected_types_waiting_to_load; 246 expected_types_waiting_to_load.Put(syncer::BOOKMARKS); 247 DataTypeManager::ConfigureResult expected_result_partially_done( 248 DataTypeManager::PARTIAL_SUCCESS, 249 types, 250 std::map<syncer::ModelType, syncer::SyncError>(), 251 expected_types_waiting_to_load, 252 syncer::ModelTypeSet()); 253 254 model_association_manager.Initialize(types); 255 model_association_manager.StopDisabledTypes(); 256 257 model_association_manager.StartAssociationAsync(types); 258 259 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). 260 WillOnce(VerifyResult(expected_result_partially_done)); 261 262 base::OneShotTimer<ModelAssociationManager>* timer = 263 model_association_manager.GetTimerForTesting(); 264 265 base::Closure task = timer->user_task(); 266 timer->Stop(); 267 task.Run(); // Bookmark load times out here. 268 269 // Apps finishes associating here. 270 GetController(controllers_, syncer::THEMES)->FinishStart( 271 DataTypeController::OK); 272 273 // At this point, BOOKMARKS is still waiting to load (as evidenced by 274 // expected_result_partially_done). If we schedule another Initialize (which 275 // could happen in practice due to reconfiguration), this should abort 276 // BOOKMARKS. Aborting will call ModelLoadCallback, but the 277 // ModelAssociationManager should be smart enough to know that this is not due 278 // to the type having completed loading. 279 EXPECT_CALL(result_processor_, OnTypesLoaded()).Times(0); 280 281 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 282 DataTypeController::MODEL_STARTING); 283 284 model_association_manager.Initialize(types); 285 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 286 DataTypeController::NOT_RUNNING); 287 288 DataTypeManager::ConfigureResult expected_result_done( 289 DataTypeManager::OK, 290 types, 291 std::map<syncer::ModelType, syncer::SyncError>(), 292 syncer::ModelTypeSet(), 293 syncer::ModelTypeSet()); 294 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). 295 WillOnce(VerifyResult(expected_result_done)); 296 297 model_association_manager.StopDisabledTypes(); 298 model_association_manager.StartAssociationAsync(types); 299 300 GetController(controllers_, 301 syncer::BOOKMARKS)->SimulateModelLoadFinishing(); 302 GetController(controllers_, syncer::BOOKMARKS)->FinishStart( 303 DataTypeController::OK); 304} 305 306// Start 2 types. One of which timeout loading. Ensure that type is 307// fully configured eventually. 308TEST_F(SyncModelAssociationManagerTest, ModelStartWithSlowLoadingType) { 309 controllers_[syncer::BOOKMARKS] = 310 new FakeDataTypeController(syncer::BOOKMARKS); 311 controllers_[syncer::APPS] = 312 new FakeDataTypeController(syncer::APPS); 313 GetController(controllers_, syncer::BOOKMARKS)->SetDelayModelLoad(); 314 ModelAssociationManager model_association_manager(&controllers_, 315 &result_processor_); 316 syncer::ModelTypeSet types; 317 types.Put(syncer::BOOKMARKS); 318 types.Put(syncer::APPS); 319 320 syncer::ModelTypeSet expected_types_waiting_to_load; 321 expected_types_waiting_to_load.Put(syncer::BOOKMARKS); 322 DataTypeManager::ConfigureResult expected_result_partially_done( 323 DataTypeManager::PARTIAL_SUCCESS, 324 types, 325 std::map<syncer::ModelType, syncer::SyncError>(), 326 expected_types_waiting_to_load, 327 syncer::ModelTypeSet()); 328 329 DataTypeManager::ConfigureResult expected_result_done( 330 DataTypeManager::OK, 331 types, 332 std::map<syncer::ModelType, syncer::SyncError>(), 333 syncer::ModelTypeSet(), 334 syncer::ModelTypeSet()); 335 336 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). 337 WillOnce(VerifyResult(expected_result_partially_done)); 338 EXPECT_CALL(result_processor_, OnTypesLoaded()); 339 340 model_association_manager.Initialize(types); 341 model_association_manager.StopDisabledTypes(); 342 model_association_manager.StartAssociationAsync(types); 343 344 base::OneShotTimer<ModelAssociationManager>* timer = 345 model_association_manager.GetTimerForTesting(); 346 347 // Note: Independent of the timeout value this test is not flaky. 348 // The reason is timer posts a task which would never be executed 349 // as we dont let the message loop run. 350 base::Closure task = timer->user_task(); 351 timer->Stop(); 352 task.Run(); 353 354 // Simulate delayed loading of bookmark model. 355 GetController(controllers_, syncer::APPS)->FinishStart( 356 DataTypeController::OK); 357 358 GetController(controllers_, 359 syncer::BOOKMARKS)->SimulateModelLoadFinishing(); 360 361 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). 362 WillOnce(VerifyResult(expected_result_done)); 363 364 // Do it once more to associate bookmarks. 365 model_association_manager.Initialize(types); 366 model_association_manager.StopDisabledTypes(); 367 model_association_manager.StartAssociationAsync(types); 368 369 GetController(controllers_, 370 syncer::BOOKMARKS)->SimulateModelLoadFinishing(); 371 372 GetController(controllers_, syncer::BOOKMARKS)->FinishStart( 373 DataTypeController::OK); 374} 375 376TEST_F(SyncModelAssociationManagerTest, StartMultipleTimes) { 377 controllers_[syncer::BOOKMARKS] = 378 new FakeDataTypeController(syncer::BOOKMARKS); 379 controllers_[syncer::APPS] = 380 new FakeDataTypeController(syncer::APPS); 381 ModelAssociationManager model_association_manager(&controllers_, 382 &result_processor_); 383 syncer::ModelTypeSet types; 384 types.Put(syncer::BOOKMARKS); 385 types.Put(syncer::APPS); 386 387 DataTypeManager::ConfigureResult result_1st( 388 DataTypeManager::OK, 389 syncer::ModelTypeSet(syncer::BOOKMARKS), 390 std::map<syncer::ModelType, syncer::SyncError>(), 391 syncer::ModelTypeSet(), 392 syncer::ModelTypeSet()); 393 DataTypeManager::ConfigureResult result_2nd( 394 DataTypeManager::OK, 395 syncer::ModelTypeSet(syncer::APPS), 396 std::map<syncer::ModelType, syncer::SyncError>(), 397 syncer::ModelTypeSet(), 398 syncer::ModelTypeSet()); 399 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). 400 Times(2). 401 WillOnce(VerifyResult(result_1st)). 402 WillOnce(VerifyResult(result_2nd)); 403 404 model_association_manager.Initialize(types); 405 model_association_manager.StopDisabledTypes(); 406 407 // Start BOOKMARKS first. 408 model_association_manager.StartAssociationAsync( 409 syncer::ModelTypeSet(syncer::BOOKMARKS)); 410 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 411 DataTypeController::ASSOCIATING); 412 EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(), 413 DataTypeController::NOT_RUNNING); 414 415 // Finish BOOKMARKS association. 416 GetController(controllers_, syncer::BOOKMARKS)->FinishStart( 417 DataTypeController::OK); 418 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), 419 DataTypeController::RUNNING); 420 EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(), 421 DataTypeController::NOT_RUNNING); 422 423 // Start APPS next. 424 model_association_manager.StartAssociationAsync( 425 syncer::ModelTypeSet(syncer::APPS)); 426 EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(), 427 DataTypeController::ASSOCIATING); 428 GetController(controllers_, syncer::APPS)->FinishStart( 429 DataTypeController::OK); 430 EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(), 431 DataTypeController::RUNNING); 432} 433 434} // namespace browser_sync 435