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 "chrome/browser/sync/glue/non_ui_data_type_controller.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/callback.h" 10 #include "base/compiler_specific.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/synchronization/waitable_event.h" 14 #include "base/test/test_timeouts.h" 15 #include "base/tracked_objects.h" 16 #include "chrome/browser/sync/glue/data_type_controller_mock.h" 17 #include "chrome/browser/sync/glue/non_ui_data_type_controller_mock.h" 18 #include "chrome/browser/sync/glue/shared_change_processor_mock.h" 19 #include "chrome/browser/sync/profile_sync_components_factory_mock.h" 20 #include "chrome/browser/sync/profile_sync_service_mock.h" 21 #include "chrome/test/base/profile_mock.h" 22 #include "content/public/test/test_browser_thread.h" 23 #include "sync/api/fake_syncable_service.h" 24 #include "sync/internal_api/public/engine/model_safe_worker.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 27 namespace browser_sync { 28 29 namespace { 30 31 using base::WaitableEvent; 32 using content::BrowserThread; 33 using syncer::AUTOFILL_PROFILE; 34 using testing::_; 35 using testing::AtLeast; 36 using testing::DoAll; 37 using testing::InvokeWithoutArgs; 38 using testing::Mock; 39 using testing::Return; 40 using testing::SetArgumentPointee; 41 using testing::StrictMock; 42 43 ACTION_P(WaitOnEvent, event) { 44 event->Wait(); 45 } 46 47 ACTION_P(SignalEvent, event) { 48 event->Signal(); 49 } 50 51 ACTION_P(SaveChangeProcessor, scoped_change_processor) { 52 scoped_change_processor->reset(arg2); 53 } 54 55 ACTION_P(GetWeakPtrToSyncableService, syncable_service) { 56 // Have to do this within an Action to ensure it's not evaluated on the wrong 57 // thread. 58 return syncable_service->AsWeakPtr(); 59 } 60 61 class NonUIDataTypeControllerFake 62 : public NonUIDataTypeController { 63 public: 64 NonUIDataTypeControllerFake( 65 ProfileSyncComponentsFactory* profile_sync_factory, 66 Profile* profile, 67 ProfileSyncService* sync_service, 68 NonUIDataTypeControllerMock* mock) 69 : NonUIDataTypeController(profile_sync_factory, 70 profile, 71 sync_service), 72 blocked_(false), 73 mock_(mock) {} 74 75 virtual syncer::ModelType type() const OVERRIDE { 76 return AUTOFILL_PROFILE; 77 } 78 virtual syncer::ModelSafeGroup model_safe_group() const OVERRIDE { 79 return syncer::GROUP_DB; 80 } 81 82 // Prevent tasks from being posted on the backend thread until 83 // UnblockBackendTasks() is called. 84 void BlockBackendTasks() { 85 blocked_ = true; 86 } 87 88 // Post pending tasks on the backend thread and start allowing tasks 89 // to be posted on the backend thread again. 90 void UnblockBackendTasks() { 91 blocked_ = false; 92 for (std::vector<PendingTask>::const_iterator it = pending_tasks_.begin(); 93 it != pending_tasks_.end(); ++it) { 94 PostTaskOnBackendThread(it->from_here, it->task); 95 } 96 pending_tasks_.clear(); 97 } 98 99 protected: 100 virtual bool PostTaskOnBackendThread( 101 const tracked_objects::Location& from_here, 102 const base::Closure& task) OVERRIDE { 103 if (blocked_) { 104 pending_tasks_.push_back(PendingTask(from_here, task)); 105 return true; 106 } else { 107 return BrowserThread::PostTask(BrowserThread::DB, from_here, task); 108 } 109 } 110 111 // We mock the following methods because their default implementations do 112 // nothing, but we still want to make sure they're called appropriately. 113 virtual bool StartModels() OVERRIDE { 114 return mock_->StartModels(); 115 } 116 virtual void StopModels() OVERRIDE { 117 mock_->StopModels(); 118 } 119 virtual void RecordUnrecoverableError( 120 const tracked_objects::Location& from_here, 121 const std::string& message) OVERRIDE { 122 mock_->RecordUnrecoverableError(from_here, message); 123 } 124 virtual void RecordAssociationTime(base::TimeDelta time) OVERRIDE { 125 mock_->RecordAssociationTime(time); 126 } 127 virtual void RecordStartFailure(DataTypeController::StartResult result) 128 OVERRIDE { 129 mock_->RecordStartFailure(result); 130 } 131 132 private: 133 virtual ~NonUIDataTypeControllerFake() {} 134 135 DISALLOW_COPY_AND_ASSIGN(NonUIDataTypeControllerFake); 136 137 struct PendingTask { 138 PendingTask(const tracked_objects::Location& from_here, 139 const base::Closure& task) 140 : from_here(from_here), task(task) {} 141 142 tracked_objects::Location from_here; 143 base::Closure task; 144 }; 145 146 bool blocked_; 147 std::vector<PendingTask> pending_tasks_; 148 NonUIDataTypeControllerMock* mock_; 149 }; 150 151 class SyncNonUIDataTypeControllerTest : public testing::Test { 152 public: 153 SyncNonUIDataTypeControllerTest() 154 : ui_thread_(BrowserThread::UI, &message_loop_), 155 db_thread_(BrowserThread::DB) {} 156 157 virtual void SetUp() OVERRIDE { 158 EXPECT_CALL(service_, GetUserShare()).WillRepeatedly( 159 Return((syncer::UserShare*)NULL)); 160 db_thread_.Start(); 161 profile_sync_factory_.reset( 162 new StrictMock<ProfileSyncComponentsFactoryMock>()); 163 change_processor_ = new SharedChangeProcessorMock(); 164 165 // All of these are refcounted, so don't need to be released. 166 dtc_mock_ = new StrictMock<NonUIDataTypeControllerMock>(); 167 non_ui_dtc_ = 168 new NonUIDataTypeControllerFake(profile_sync_factory_.get(), 169 &profile_, 170 &service_, 171 dtc_mock_.get()); 172 } 173 174 virtual void TearDown() OVERRIDE { 175 db_thread_.Stop(); 176 } 177 178 void WaitForDTC() { 179 WaitableEvent done(true, false); 180 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 181 base::Bind(&SyncNonUIDataTypeControllerTest::SignalDone, 182 &done)); 183 done.TimedWait(TestTimeouts::action_timeout()); 184 if (!done.IsSignaled()) { 185 ADD_FAILURE() << "Timed out waiting for DB thread to finish."; 186 } 187 base::MessageLoop::current()->RunUntilIdle(); 188 } 189 190 protected: 191 void SetStartExpectations() { 192 EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(true)); 193 EXPECT_CALL(model_load_callback_, Run(_, _)); 194 EXPECT_CALL(*profile_sync_factory_, 195 CreateSharedChangeProcessor()). 196 WillOnce(Return(change_processor_.get())); 197 } 198 199 void SetAssociateExpectations() { 200 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _)) 201 .WillOnce(GetWeakPtrToSyncableService(&syncable_service_)); 202 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) 203 .WillOnce(Return(true)); 204 EXPECT_CALL(*change_processor_.get(), ActivateDataType(_)); 205 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_)) 206 .WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true))); 207 EXPECT_CALL(*change_processor_.get(), GetSyncData(_)) 208 .WillOnce(Return(syncer::SyncError())); 209 EXPECT_CALL(*change_processor_.get(), GetSyncCount()).WillOnce(Return(0)); 210 EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_)); 211 } 212 213 void SetActivateExpectations(DataTypeController::StartResult result) { 214 EXPECT_CALL(start_callback_, Run(result,_,_)); 215 } 216 217 void SetStopExpectations() { 218 EXPECT_CALL(*dtc_mock_.get(), StopModels()); 219 EXPECT_CALL(*change_processor_.get(), Disconnect()).WillOnce(Return(true)); 220 EXPECT_CALL(service_, DeactivateDataType(_)); 221 } 222 223 void SetStartFailExpectations(DataTypeController::StartResult result) { 224 EXPECT_CALL(*dtc_mock_.get(), StopModels()).Times(AtLeast(1)); 225 if (DataTypeController::IsUnrecoverableResult(result)) 226 EXPECT_CALL(*dtc_mock_.get(), RecordUnrecoverableError(_, _)); 227 EXPECT_CALL(*dtc_mock_.get(), RecordStartFailure(result)); 228 EXPECT_CALL(start_callback_, Run(result, _, _)); 229 } 230 231 void Start() { 232 non_ui_dtc_->LoadModels( 233 base::Bind(&ModelLoadCallbackMock::Run, 234 base::Unretained(&model_load_callback_))); 235 non_ui_dtc_->StartAssociating( 236 base::Bind(&StartCallbackMock::Run, 237 base::Unretained(&start_callback_))); 238 } 239 240 static void SignalDone(WaitableEvent* done) { 241 done->Signal(); 242 } 243 244 base::MessageLoopForUI message_loop_; 245 content::TestBrowserThread ui_thread_; 246 content::TestBrowserThread db_thread_; 247 ProfileMock profile_; 248 scoped_ptr<ProfileSyncComponentsFactoryMock> profile_sync_factory_; 249 StrictMock<ProfileSyncServiceMock> service_; 250 StartCallbackMock start_callback_; 251 ModelLoadCallbackMock model_load_callback_; 252 // Must be destroyed after non_ui_dtc_. 253 syncer::FakeSyncableService syncable_service_; 254 scoped_refptr<NonUIDataTypeControllerFake> non_ui_dtc_; 255 scoped_refptr<NonUIDataTypeControllerMock> dtc_mock_; 256 scoped_refptr<SharedChangeProcessorMock> change_processor_; 257 scoped_ptr<syncer::SyncChangeProcessor> saved_change_processor_; 258 }; 259 260 TEST_F(SyncNonUIDataTypeControllerTest, StartOk) { 261 SetStartExpectations(); 262 SetAssociateExpectations(); 263 SetActivateExpectations(DataTypeController::OK); 264 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 265 Start(); 266 WaitForDTC(); 267 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); 268 } 269 270 TEST_F(SyncNonUIDataTypeControllerTest, StartFirstRun) { 271 SetStartExpectations(); 272 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _)) 273 .WillOnce(GetWeakPtrToSyncableService(&syncable_service_)); 274 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) 275 .WillOnce(Return(true)); 276 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_)) 277 .WillOnce(DoAll(SetArgumentPointee<0>(false), Return(true))); 278 EXPECT_CALL(*change_processor_.get(), GetSyncData(_)) 279 .WillOnce(Return(syncer::SyncError())); 280 EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_)); 281 SetActivateExpectations(DataTypeController::OK_FIRST_RUN); 282 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 283 Start(); 284 WaitForDTC(); 285 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); 286 } 287 288 // Start the DTC and have StartModels() return false. Then, stop the 289 // DTC without finishing model startup. It should stop cleanly. 290 TEST_F(SyncNonUIDataTypeControllerTest, AbortDuringStartModels) { 291 EXPECT_CALL(*profile_sync_factory_, 292 CreateSharedChangeProcessor()). 293 WillOnce(Return(change_processor_.get())); 294 EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(false)); 295 EXPECT_CALL(*dtc_mock_.get(), StopModels()); 296 EXPECT_CALL(model_load_callback_, Run(_, _)); 297 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 298 non_ui_dtc_->LoadModels( 299 base::Bind(&ModelLoadCallbackMock::Run, 300 base::Unretained(&model_load_callback_))); 301 WaitForDTC(); 302 EXPECT_EQ(DataTypeController::MODEL_STARTING, non_ui_dtc_->state()); 303 non_ui_dtc_->Stop(); 304 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 305 } 306 307 // Start the DTC and have MergeDataAndStartSyncing() return an error. 308 // The DTC should become disabled, and the DTC should still stop 309 // cleanly. 310 TEST_F(SyncNonUIDataTypeControllerTest, StartAssociationFailed) { 311 SetStartExpectations(); 312 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _)) 313 .WillOnce(GetWeakPtrToSyncableService(&syncable_service_)); 314 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) 315 .WillOnce(Return(true)); 316 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_)) 317 .WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true))); 318 EXPECT_CALL(*change_processor_.get(), GetSyncData(_)) 319 .WillOnce(Return(syncer::SyncError())); 320 EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_)); 321 SetStartFailExpectations(DataTypeController::ASSOCIATION_FAILED); 322 // Set up association to fail with an association failed error. 323 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 324 syncable_service_.set_merge_data_and_start_syncing_error( 325 syncer::SyncError(FROM_HERE, 326 syncer::SyncError::DATATYPE_ERROR, 327 "Sync Error", 328 non_ui_dtc_->type())); 329 Start(); 330 WaitForDTC(); 331 EXPECT_EQ(DataTypeController::DISABLED, non_ui_dtc_->state()); 332 non_ui_dtc_->Stop(); 333 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 334 } 335 336 TEST_F(SyncNonUIDataTypeControllerTest, 337 StartAssociationTriggersUnrecoverableError) { 338 SetStartExpectations(); 339 SetStartFailExpectations(DataTypeController::UNRECOVERABLE_ERROR); 340 // Set up association to fail with an unrecoverable error. 341 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _)) 342 .WillOnce(GetWeakPtrToSyncableService(&syncable_service_)); 343 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) 344 .WillRepeatedly(Return(true)); 345 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_)) 346 .WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false))); 347 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 348 Start(); 349 WaitForDTC(); 350 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 351 } 352 353 TEST_F(SyncNonUIDataTypeControllerTest, 354 StartAssociationCryptoNotReady) { 355 SetStartExpectations(); 356 SetStartFailExpectations(DataTypeController::NEEDS_CRYPTO); 357 // Set up association to fail with a NEEDS_CRYPTO error. 358 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _)) 359 .WillOnce(GetWeakPtrToSyncableService(&syncable_service_)); 360 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) 361 .WillRepeatedly(Return(false)); 362 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 363 Start(); 364 WaitForDTC(); 365 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 366 } 367 368 // Trigger a Stop() call when we check if the model associator has user created 369 // nodes. 370 TEST_F(SyncNonUIDataTypeControllerTest, AbortDuringAssociation) { 371 WaitableEvent wait_for_db_thread_pause(false, false); 372 WaitableEvent pause_db_thread(false, false); 373 374 SetStartExpectations(); 375 SetStartFailExpectations(DataTypeController::ABORTED); 376 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _)) 377 .WillOnce(GetWeakPtrToSyncableService(&syncable_service_)); 378 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) 379 .WillOnce(Return(true)); 380 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_)) 381 .WillOnce(DoAll(SignalEvent(&wait_for_db_thread_pause), 382 WaitOnEvent(&pause_db_thread), 383 SetArgumentPointee<0>(true), 384 Return(true))); 385 EXPECT_CALL(*change_processor_.get(), GetSyncData(_)).WillOnce( 386 Return(syncer::SyncError(FROM_HERE, 387 syncer::SyncError::DATATYPE_ERROR, 388 "Disconnected.", 389 AUTOFILL_PROFILE))); 390 EXPECT_CALL(*change_processor_.get(), Disconnect()) 391 .WillOnce(DoAll(SignalEvent(&pause_db_thread), Return(true))); 392 EXPECT_CALL(service_, DeactivateDataType(_)); 393 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 394 Start(); 395 wait_for_db_thread_pause.Wait(); 396 non_ui_dtc_->Stop(); 397 WaitForDTC(); 398 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 399 } 400 401 // Start the DTC while the backend tasks are blocked. Then stop the DTC before 402 // the backend tasks get a chance to run. The DTC should have no interaction 403 // with the profile sync factory or profile sync service once stopped. 404 TEST_F(SyncNonUIDataTypeControllerTest, StartAfterSyncShutdown) { 405 non_ui_dtc_->BlockBackendTasks(); 406 407 SetStartExpectations(); 408 // We don't expect StopSyncing to be called because local_service_ will never 409 // have been set. 410 EXPECT_CALL(*change_processor_.get(), Disconnect()).WillOnce(Return(true)); 411 EXPECT_CALL(*dtc_mock_.get(), StopModels()); 412 EXPECT_CALL(service_, DeactivateDataType(_)); 413 EXPECT_CALL(*dtc_mock_.get(), 414 RecordStartFailure(DataTypeController::ABORTED)); 415 EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED, _, _)); 416 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 417 Start(); 418 non_ui_dtc_->Stop(); 419 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 420 Mock::VerifyAndClearExpectations(&profile_sync_factory_); 421 Mock::VerifyAndClearExpectations(&service_); 422 Mock::VerifyAndClearExpectations(change_processor_.get()); 423 Mock::VerifyAndClearExpectations(dtc_mock_.get()); 424 425 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _)) 426 .WillOnce(Return(base::WeakPtr<syncer::SyncableService>())); 427 non_ui_dtc_->UnblockBackendTasks(); 428 WaitForDTC(); 429 } 430 431 TEST_F(SyncNonUIDataTypeControllerTest, Stop) { 432 SetStartExpectations(); 433 SetAssociateExpectations(); 434 SetActivateExpectations(DataTypeController::OK); 435 SetStopExpectations(); 436 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 437 Start(); 438 WaitForDTC(); 439 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); 440 non_ui_dtc_->Stop(); 441 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 442 } 443 444 // Start the DTC then block its backend tasks. While its backend 445 // tasks are blocked, stop and start it again, then unblock its 446 // backend tasks. The (delayed) running of the backend tasks from the 447 // stop after the restart shouldn't cause any problems. 448 TEST_F(SyncNonUIDataTypeControllerTest, StopStart) { 449 SetStartExpectations(); 450 SetAssociateExpectations(); 451 SetActivateExpectations(DataTypeController::OK); 452 SetStopExpectations(); 453 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 454 Start(); 455 WaitForDTC(); 456 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); 457 458 non_ui_dtc_->BlockBackendTasks(); 459 non_ui_dtc_->Stop(); 460 Mock::VerifyAndClearExpectations(&profile_sync_factory_); 461 SetStartExpectations(); 462 SetAssociateExpectations(); 463 SetActivateExpectations(DataTypeController::OK); 464 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 465 Start(); 466 non_ui_dtc_->UnblockBackendTasks(); 467 468 WaitForDTC(); 469 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); 470 } 471 472 TEST_F(SyncNonUIDataTypeControllerTest, 473 OnSingleDatatypeUnrecoverableError) { 474 SetStartExpectations(); 475 SetAssociateExpectations(); 476 SetActivateExpectations(DataTypeController::OK); 477 EXPECT_CALL(*dtc_mock_.get(), RecordUnrecoverableError(_, "Test")); 478 EXPECT_CALL(service_, DisableBrokenDatatype(_, _, _)).WillOnce( 479 InvokeWithoutArgs(non_ui_dtc_.get(), &NonUIDataTypeController::Stop)); 480 SetStopExpectations(); 481 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 482 Start(); 483 WaitForDTC(); 484 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); 485 // This should cause non_ui_dtc_->Stop() to be called. 486 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, base::Bind( 487 &NonUIDataTypeControllerFake:: 488 OnSingleDatatypeUnrecoverableError, 489 non_ui_dtc_.get(), 490 FROM_HERE, 491 std::string("Test"))); 492 WaitForDTC(); 493 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); 494 } 495 496 } // namespace 497 498 } // namespace browser_sync 499