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 "testing/gtest/include/gtest/gtest.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/change_processor_mock.h" 17#include "chrome/browser/sync/glue/data_type_controller_mock.h" 18#include "chrome/browser/sync/glue/model_associator_mock.h" 19#include "chrome/browser/sync/glue/non_frontend_data_type_controller.h" 20#include "chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h" 21#include "chrome/browser/sync/profile_sync_components_factory_mock.h" 22#include "chrome/browser/sync/profile_sync_service_mock.h" 23#include "chrome/test/base/profile_mock.h" 24#include "content/public/test/test_browser_thread.h" 25#include "sync/internal_api/public/engine/model_safe_worker.h" 26 27using base::WaitableEvent; 28using browser_sync::ChangeProcessorMock; 29using browser_sync::DataTypeController; 30using syncer::GROUP_DB; 31using browser_sync::NonFrontendDataTypeController; 32using browser_sync::NonFrontendDataTypeControllerMock; 33using browser_sync::ModelAssociatorMock; 34using browser_sync::ModelLoadCallbackMock; 35using browser_sync::StartCallbackMock; 36using content::BrowserThread; 37using testing::_; 38using testing::DoAll; 39using testing::InvokeWithoutArgs; 40using testing::Return; 41using testing::SetArgumentPointee; 42using testing::StrictMock; 43 44ACTION_P(WaitOnEvent, event) { 45 event->Wait(); 46} 47 48ACTION_P(SignalEvent, event) { 49 event->Signal(); 50} 51 52class NonFrontendDataTypeControllerFake : public NonFrontendDataTypeController { 53 public: 54 NonFrontendDataTypeControllerFake( 55 ProfileSyncComponentsFactory* profile_sync_factory, 56 Profile* profile, 57 ProfileSyncService* sync_service, 58 NonFrontendDataTypeControllerMock* mock) 59 : NonFrontendDataTypeController(profile_sync_factory, 60 profile, 61 sync_service), 62 mock_(mock) {} 63 64 virtual syncer::ModelType type() const OVERRIDE { return syncer::BOOKMARKS; } 65 virtual syncer::ModelSafeGroup model_safe_group() const OVERRIDE { 66 return syncer::GROUP_DB; 67 } 68 69 private: 70 virtual ~NonFrontendDataTypeControllerFake() {} 71 72 virtual ProfileSyncComponentsFactory::SyncComponents 73 CreateSyncComponents() OVERRIDE { 74 return profile_sync_factory()-> 75 CreateBookmarkSyncComponents(profile_sync_service(), this); 76 } 77 78 virtual bool PostTaskOnBackendThread( 79 const tracked_objects::Location& from_here, 80 const base::Closure& task) OVERRIDE { 81 return BrowserThread::PostTask(BrowserThread::DB, from_here, task); 82 } 83 84 // We mock the following methods because their default implementations do 85 // nothing, but we still want to make sure they're called appropriately. 86 virtual bool StartModels() OVERRIDE { 87 return mock_->StartModels(); 88 } 89 virtual void RecordUnrecoverableError( 90 const tracked_objects::Location& from_here, 91 const std::string& message) OVERRIDE { 92 mock_->RecordUnrecoverableError(from_here, message); 93 } 94 virtual void RecordAssociationTime(base::TimeDelta time) OVERRIDE { 95 mock_->RecordAssociationTime(time); 96 } 97 virtual void RecordStartFailure( 98 DataTypeController::StartResult result) OVERRIDE { 99 mock_->RecordStartFailure(result); 100 } 101 virtual void DisconnectProcessor( 102 browser_sync::ChangeProcessor* processor) OVERRIDE{ 103 mock_->DisconnectProcessor(processor); 104 } 105 106 private: 107 NonFrontendDataTypeControllerMock* mock_; 108}; 109 110class SyncNonFrontendDataTypeControllerTest : public testing::Test { 111 public: 112 SyncNonFrontendDataTypeControllerTest() 113 : ui_thread_(BrowserThread::UI, &message_loop_), 114 db_thread_(BrowserThread::DB), 115 service_(&profile_), 116 model_associator_(NULL), 117 change_processor_(NULL) {} 118 119 virtual void SetUp() { 120 db_thread_.Start(); 121 profile_sync_factory_.reset( 122 new StrictMock<ProfileSyncComponentsFactoryMock>()); 123 124 // All of these are refcounted, so don't need to be released. 125 dtc_mock_ = new StrictMock<NonFrontendDataTypeControllerMock>(); 126 non_frontend_dtc_ = 127 new NonFrontendDataTypeControllerFake(profile_sync_factory_.get(), 128 &profile_, 129 &service_, 130 dtc_mock_.get()); 131 } 132 133 virtual void TearDown() { 134 if (non_frontend_dtc_->state() != 135 NonFrontendDataTypeController::NOT_RUNNING) { 136 non_frontend_dtc_->Stop(); 137 } 138 db_thread_.Stop(); 139 } 140 141 protected: 142 void SetStartExpectations() { 143 EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(true)); 144 EXPECT_CALL(model_load_callback_, Run(_, _)); 145 model_associator_ = new ModelAssociatorMock(); 146 change_processor_ = new ChangeProcessorMock(); 147 EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _)). 148 WillOnce(Return(ProfileSyncComponentsFactory::SyncComponents( 149 model_associator_, change_processor_))); 150 } 151 152 void SetAssociateExpectations() { 153 EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). 154 WillOnce(Return(true)); 155 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 156 WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true))); 157 EXPECT_CALL(*model_associator_, AssociateModels(_, _)). 158 WillOnce(Return(syncer::SyncError())); 159 EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_)); 160 } 161 162 void SetActivateExpectations(DataTypeController::StartResult result) { 163 EXPECT_CALL(service_, ActivateDataType(_, _, _)); 164 EXPECT_CALL(start_callback_, Run(result, _, _)); 165 } 166 167 void SetStopExpectations() { 168 EXPECT_CALL(*dtc_mock_.get(), DisconnectProcessor(_)); 169 EXPECT_CALL(service_, DeactivateDataType(_)); 170 EXPECT_CALL(*model_associator_, DisassociateModels()). 171 WillOnce(Return(syncer::SyncError())); 172 } 173 174 void SetStartFailExpectations(DataTypeController::StartResult result) { 175 if (DataTypeController::IsUnrecoverableResult(result)) 176 EXPECT_CALL(*dtc_mock_.get(), RecordUnrecoverableError(_, _)); 177 if (model_associator_) { 178 EXPECT_CALL(*model_associator_, DisassociateModels()). 179 WillOnce(Return(syncer::SyncError())); 180 } 181 EXPECT_CALL(*dtc_mock_.get(), RecordStartFailure(result)); 182 EXPECT_CALL(start_callback_, Run(result, _, _)); 183 } 184 185 static void SignalDone(WaitableEvent* done) { 186 done->Signal(); 187 } 188 189 void WaitForDTC() { 190 WaitableEvent done(true, false); 191 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 192 base::Bind(&SyncNonFrontendDataTypeControllerTest::SignalDone, &done)); 193 done.TimedWait(TestTimeouts::action_timeout()); 194 if (!done.IsSignaled()) { 195 ADD_FAILURE() << "Timed out waiting for DB thread to finish."; 196 } 197 base::MessageLoop::current()->RunUntilIdle(); 198 } 199 200 void Start() { 201 non_frontend_dtc_->LoadModels( 202 base::Bind(&ModelLoadCallbackMock::Run, 203 base::Unretained(&model_load_callback_))); 204 non_frontend_dtc_->StartAssociating( 205 base::Bind(&StartCallbackMock::Run, 206 base::Unretained(&start_callback_))); 207 } 208 209 base::MessageLoopForUI message_loop_; 210 content::TestBrowserThread ui_thread_; 211 content::TestBrowserThread db_thread_; 212 scoped_refptr<NonFrontendDataTypeControllerFake> non_frontend_dtc_; 213 scoped_ptr<ProfileSyncComponentsFactoryMock> profile_sync_factory_; 214 scoped_refptr<NonFrontendDataTypeControllerMock> dtc_mock_; 215 ProfileMock profile_; 216 ProfileSyncServiceMock service_; 217 ModelAssociatorMock* model_associator_; 218 ChangeProcessorMock* change_processor_; 219 StartCallbackMock start_callback_; 220 ModelLoadCallbackMock model_load_callback_; 221}; 222 223TEST_F(SyncNonFrontendDataTypeControllerTest, StartOk) { 224 SetStartExpectations(); 225 SetAssociateExpectations(); 226 SetActivateExpectations(DataTypeController::OK); 227 SetStopExpectations(); 228 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 229 Start(); 230 WaitForDTC(); 231 EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state()); 232} 233 234TEST_F(SyncNonFrontendDataTypeControllerTest, StartFirstRun) { 235 SetStartExpectations(); 236 EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). 237 WillOnce(Return(true)); 238 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 239 WillOnce(DoAll(SetArgumentPointee<0>(false), Return(true))); 240 EXPECT_CALL(*model_associator_, AssociateModels(_, _)). 241 WillOnce(Return(syncer::SyncError())); 242 EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_)); 243 SetActivateExpectations(DataTypeController::OK_FIRST_RUN); 244 SetStopExpectations(); 245 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 246 Start(); 247 WaitForDTC(); 248 EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state()); 249} 250 251TEST_F(SyncNonFrontendDataTypeControllerTest, StartAssociationFailed) { 252 SetStartExpectations(); 253 EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). 254 WillOnce(Return(true)); 255 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 256 WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true))); 257 EXPECT_CALL(*model_associator_, AssociateModels(_, _)). 258 WillOnce( 259 Return(syncer::SyncError(FROM_HERE, 260 syncer::SyncError::DATATYPE_ERROR, 261 "Error", 262 syncer::BOOKMARKS))); 263 EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_)); 264 SetStartFailExpectations(DataTypeController::ASSOCIATION_FAILED); 265 // Set up association to fail with an association failed error. 266 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 267 Start(); 268 WaitForDTC(); 269 EXPECT_EQ(DataTypeController::DISABLED, non_frontend_dtc_->state()); 270} 271 272TEST_F(SyncNonFrontendDataTypeControllerTest, 273 StartAssociationTriggersUnrecoverableError) { 274 SetStartExpectations(); 275 SetStartFailExpectations(DataTypeController::UNRECOVERABLE_ERROR); 276 // Set up association to fail with an unrecoverable error. 277 EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). 278 WillRepeatedly(Return(true)); 279 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 280 WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false))); 281 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 282 Start(); 283 WaitForDTC(); 284 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 285} 286 287TEST_F(SyncNonFrontendDataTypeControllerTest, StartAssociationCryptoNotReady) { 288 SetStartExpectations(); 289 SetStartFailExpectations(DataTypeController::NEEDS_CRYPTO); 290 // Set up association to fail with a NEEDS_CRYPTO error. 291 EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). 292 WillRepeatedly(Return(false)); 293 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 294 Start(); 295 WaitForDTC(); 296 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 297} 298 299// Trigger a Stop() call when we check if the model associator has user created 300// nodes. 301TEST_F(SyncNonFrontendDataTypeControllerTest, AbortDuringAssociationInactive) { 302 WaitableEvent wait_for_db_thread_pause(false, false); 303 WaitableEvent pause_db_thread(false, false); 304 305 SetStartExpectations(); 306 EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). 307 WillOnce(Return(true)); 308 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 309 WillOnce(DoAll( 310 SignalEvent(&wait_for_db_thread_pause), 311 WaitOnEvent(&pause_db_thread), 312 SetArgumentPointee<0>(true), 313 Return(true))); 314 EXPECT_CALL(*model_associator_, AbortAssociation()).WillOnce( 315 SignalEvent(&pause_db_thread)); 316 EXPECT_CALL(*model_associator_, AssociateModels(_, _)). 317 WillOnce(Return(syncer::SyncError())); 318 EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED,_,_)); 319 EXPECT_CALL(*dtc_mock_.get(), 320 RecordStartFailure(DataTypeController::ABORTED)); 321 SetStopExpectations(); 322 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 323 Start(); 324 wait_for_db_thread_pause.Wait(); 325 non_frontend_dtc_->Stop(); 326 WaitForDTC(); 327 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 328} 329 330// Same as above but abort during the Activate call. 331TEST_F(SyncNonFrontendDataTypeControllerTest, AbortDuringAssociationActivated) { 332 WaitableEvent wait_for_association_starts(false, false); 333 WaitableEvent wait_for_dtc_stop(false, false); 334 335 SetStartExpectations(); 336 EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). 337 WillOnce(Return(true)); 338 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 339 WillOnce(DoAll( 340 SetArgumentPointee<0>(true), 341 Return(true))); 342 EXPECT_CALL(*model_associator_, AbortAssociation()); 343 EXPECT_CALL(*model_associator_, AssociateModels(_, _)). 344 WillOnce(DoAll( 345 SignalEvent(&wait_for_association_starts), 346 WaitOnEvent(&wait_for_dtc_stop), 347 Return(syncer::SyncError()))); 348 EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED,_,_)); 349 EXPECT_CALL(*dtc_mock_.get(), 350 RecordStartFailure(DataTypeController::ABORTED)); 351 SetStopExpectations(); 352 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 353 Start(); 354 wait_for_association_starts.Wait(); 355 non_frontend_dtc_->Stop(); 356 wait_for_dtc_stop.Signal(); 357 WaitForDTC(); 358 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 359} 360 361TEST_F(SyncNonFrontendDataTypeControllerTest, Stop) { 362 SetStartExpectations(); 363 SetAssociateExpectations(); 364 SetActivateExpectations(DataTypeController::OK); 365 SetStopExpectations(); 366 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 367 Start(); 368 WaitForDTC(); 369 EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state()); 370 non_frontend_dtc_->Stop(); 371 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 372} 373 374TEST_F(SyncNonFrontendDataTypeControllerTest, 375 OnSingleDatatypeUnrecoverableError) { 376 SetStartExpectations(); 377 SetAssociateExpectations(); 378 SetActivateExpectations(DataTypeController::OK); 379 EXPECT_CALL(*dtc_mock_.get(), RecordUnrecoverableError(_, "Test")); 380 EXPECT_CALL(service_, DisableBrokenDatatype(_, _, _)) 381 .WillOnce(InvokeWithoutArgs(non_frontend_dtc_.get(), 382 &NonFrontendDataTypeController::Stop)); 383 SetStopExpectations(); 384 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 385 Start(); 386 WaitForDTC(); 387 EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state()); 388 // This should cause non_frontend_dtc_->Stop() to be called. 389 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, base::Bind( 390 &NonFrontendDataTypeControllerFake::OnSingleDatatypeUnrecoverableError, 391 non_frontend_dtc_.get(), 392 FROM_HERE, 393 std::string("Test"))); 394 WaitForDTC(); 395 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); 396} 397