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