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.
5 #include "components/sync_driver/non_ui_data_type_controller.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"
26 namespace sync_driver {
30 using base::WaitableEvent;
31 using syncer::AUTOFILL_PROFILE;
33 using testing::AtLeast;
35 using testing::InvokeWithoutArgs;
37 using testing::Return;
38 using testing::SetArgumentPointee;
39 using testing::StrictMock;
41 ACTION_P(WaitOnEvent, event) {
45 ACTION_P(SignalEvent, event) {
49 ACTION_P(SaveChangeProcessor, scoped_change_processor) {
50 scoped_change_processor->reset(arg2);
53 ACTION_P(GetWeakPtrToSyncableService, syncable_service) {
54 // Have to do this within an Action to ensure it's not evaluated on the wrong
56 return syncable_service->AsWeakPtr();
59 class SharedChangeProcessorMock : public SharedChangeProcessor {
61 SharedChangeProcessorMock() {}
63 MOCK_METHOD6(Connect, base::WeakPtr<syncer::SyncableService>(
64 SyncApiComponentFactory*,
65 GenericChangeProcessorFactory*,
67 DataTypeErrorHandler*,
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,
80 MOCK_METHOD0(CryptoReadyIfNecessary, bool());
81 MOCK_CONST_METHOD1(GetDataTypeContext, bool(std::string*));
84 virtual ~SharedChangeProcessorMock() {}
85 MOCK_METHOD2(OnUnrecoverableError, void(const tracked_objects::Location&,
89 DISALLOW_COPY_AND_ASSIGN(SharedChangeProcessorMock);
92 class NonUIDataTypeControllerFake
93 : public NonUIDataTypeController {
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(),
106 change_processor_(change_processor),
107 backend_loop_(backend_loop) {}
109 virtual syncer::ModelType type() const OVERRIDE {
110 return AUTOFILL_PROFILE;
112 virtual syncer::ModelSafeGroup model_safe_group() const OVERRIDE {
113 return syncer::GROUP_DB;
116 // Prevent tasks from being posted on the backend thread until
117 // UnblockBackendTasks() is called.
118 void BlockBackendTasks() {
122 // Post pending tasks on the backend thread and start allowing tasks
123 // to be posted on the backend thread again.
124 void UnblockBackendTasks() {
126 for (std::vector<PendingTask>::const_iterator it = pending_tasks_.begin();
127 it != pending_tasks_.end(); ++it) {
128 PostTaskOnBackendThread(it->from_here, it->task);
130 pending_tasks_.clear();
133 virtual SharedChangeProcessor* CreateSharedChangeProcessor() OVERRIDE {
134 return change_processor_.get();
138 virtual bool PostTaskOnBackendThread(
139 const tracked_objects::Location& from_here,
140 const base::Closure& task) OVERRIDE {
142 pending_tasks_.push_back(PendingTask(from_here, task));
145 return backend_loop_->PostTask(from_here, task);
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();
154 virtual void StopModels() OVERRIDE {
157 virtual void RecordAssociationTime(base::TimeDelta time) OVERRIDE {
158 mock_->RecordAssociationTime(time);
160 virtual void RecordStartFailure(DataTypeController::ConfigureResult result)
162 mock_->RecordStartFailure(result);
166 virtual ~NonUIDataTypeControllerFake() {}
168 DISALLOW_COPY_AND_ASSIGN(NonUIDataTypeControllerFake);
171 PendingTask(const tracked_objects::Location& from_here,
172 const base::Closure& task)
173 : from_here(from_here), task(task) {}
175 tracked_objects::Location from_here;
180 std::vector<PendingTask> pending_tasks_;
181 NonUIDataTypeControllerMock* mock_;
182 scoped_refptr<SharedChangeProcessor> change_processor_;
183 scoped_refptr<base::MessageLoopProxy> backend_loop_;
186 class SyncNonUIDataTypeControllerTest : public testing::Test {
188 SyncNonUIDataTypeControllerTest()
189 : backend_thread_("dbthread") {}
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>();
197 new NonUIDataTypeControllerFake(NULL,
199 change_processor_.get(),
200 backend_thread_.message_loop_proxy());
203 virtual void TearDown() OVERRIDE {
204 backend_thread_.Stop();
208 WaitableEvent done(true, false);
209 backend_thread_.message_loop_proxy()->PostTask(
211 base::Bind(&SyncNonUIDataTypeControllerTest::SignalDone,
213 done.TimedWait(TestTimeouts::action_timeout());
214 if (!done.IsSignaled()) {
215 ADD_FAILURE() << "Timed out waiting for DB thread to finish.";
217 base::MessageLoop::current()->RunUntilIdle();
221 void SetStartExpectations() {
222 EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(true));
223 EXPECT_CALL(model_load_callback_, Run(_, _));
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(_));
239 void SetActivateExpectations(DataTypeController::ConfigureResult result) {
240 EXPECT_CALL(start_callback_, Run(result,_,_));
243 void SetStopExpectations() {
244 EXPECT_CALL(*dtc_mock_.get(), StopModels());
245 EXPECT_CALL(*change_processor_.get(), Disconnect()).WillOnce(Return(true));
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, _, _));
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_)));
263 static void SignalDone(WaitableEvent* done) {
267 base::MessageLoopForUI message_loop_;
268 base::Thread backend_thread_;
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_;
280 TEST_F(SyncNonUIDataTypeControllerTest, StartOk) {
281 SetStartExpectations();
282 SetAssociateExpectations();
283 SetActivateExpectations(DataTypeController::OK);
284 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
287 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state());
290 TEST_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());
305 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state());
308 // Start the DTC and have StartModels() return false. Then, stop the
309 // DTC without finishing model startup. It should stop cleanly.
310 TEST_F(SyncNonUIDataTypeControllerTest, AbortDuringStartModels) {
311 EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(false));
312 EXPECT_CALL(*dtc_mock_.get(), StopModels());
313 EXPECT_CALL(model_load_callback_, Run(_, _));
314 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
315 non_ui_dtc_->LoadModels(
316 base::Bind(&ModelLoadCallbackMock::Run,
317 base::Unretained(&model_load_callback_)));
319 EXPECT_EQ(DataTypeController::MODEL_STARTING, non_ui_dtc_->state());
321 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
324 // Start the DTC and have MergeDataAndStartSyncing() return an error.
325 // The DTC should become disabled, and the DTC should still stop
327 TEST_F(SyncNonUIDataTypeControllerTest, StartAssociationFailed) {
328 SetStartExpectations();
329 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _, _))
330 .WillOnce(GetWeakPtrToSyncableService(&syncable_service_));
331 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary())
332 .WillOnce(Return(true));
333 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_))
334 .WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
335 EXPECT_CALL(*change_processor_.get(), GetAllSyncDataReturnError(_,_))
336 .WillOnce(Return(syncer::SyncError()));
337 EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
338 SetStartFailExpectations(DataTypeController::ASSOCIATION_FAILED);
339 // Set up association to fail with an association failed error.
340 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
341 syncable_service_.set_merge_data_and_start_syncing_error(
342 syncer::SyncError(FROM_HERE,
343 syncer::SyncError::DATATYPE_ERROR,
345 non_ui_dtc_->type()));
348 EXPECT_EQ(DataTypeController::DISABLED, non_ui_dtc_->state());
350 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
353 TEST_F(SyncNonUIDataTypeControllerTest,
354 StartAssociationTriggersUnrecoverableError) {
355 SetStartExpectations();
356 SetStartFailExpectations(DataTypeController::UNRECOVERABLE_ERROR);
357 // Set up association to fail with an unrecoverable error.
358 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _, _))
359 .WillOnce(GetWeakPtrToSyncableService(&syncable_service_));
360 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary())
361 .WillRepeatedly(Return(true));
362 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_))
363 .WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false)));
364 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
367 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
370 TEST_F(SyncNonUIDataTypeControllerTest,
371 StartAssociationCryptoNotReady) {
372 SetStartExpectations();
373 SetStartFailExpectations(DataTypeController::NEEDS_CRYPTO);
374 // Set up association to fail with a NEEDS_CRYPTO error.
375 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _, _))
376 .WillOnce(GetWeakPtrToSyncableService(&syncable_service_));
377 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary())
378 .WillRepeatedly(Return(false));
379 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
382 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
385 // Trigger a Stop() call when we check if the model associator has user created
387 TEST_F(SyncNonUIDataTypeControllerTest, AbortDuringAssociation) {
388 WaitableEvent wait_for_db_thread_pause(false, false);
389 WaitableEvent pause_db_thread(false, false);
391 SetStartExpectations();
392 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _, _))
393 .WillOnce(GetWeakPtrToSyncableService(&syncable_service_));
394 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary())
395 .WillOnce(Return(true));
396 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_))
397 .WillOnce(DoAll(SignalEvent(&wait_for_db_thread_pause),
398 WaitOnEvent(&pause_db_thread),
399 SetArgumentPointee<0>(true),
401 EXPECT_CALL(*change_processor_.get(), GetAllSyncDataReturnError(_,_))
403 Return(syncer::SyncError(FROM_HERE,
404 syncer::SyncError::DATATYPE_ERROR,
407 EXPECT_CALL(*change_processor_.get(), Disconnect())
408 .WillOnce(DoAll(SignalEvent(&pause_db_thread), Return(true)));
409 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
411 wait_for_db_thread_pause.Wait();
414 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
417 // Start the DTC while the backend tasks are blocked. Then stop the DTC before
418 // the backend tasks get a chance to run.
419 TEST_F(SyncNonUIDataTypeControllerTest, StartAfterSyncShutdown) {
420 non_ui_dtc_->BlockBackendTasks();
422 SetStartExpectations();
423 // We don't expect StopSyncing to be called because local_service_ will never
425 EXPECT_CALL(*change_processor_.get(), Disconnect()).WillOnce(Return(true));
426 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
429 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
430 Mock::VerifyAndClearExpectations(change_processor_.get());
431 Mock::VerifyAndClearExpectations(dtc_mock_.get());
433 EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _, _))
434 .WillOnce(Return(base::WeakPtr<syncer::SyncableService>()));
435 non_ui_dtc_->UnblockBackendTasks();
439 TEST_F(SyncNonUIDataTypeControllerTest, Stop) {
440 SetStartExpectations();
441 SetAssociateExpectations();
442 SetActivateExpectations(DataTypeController::OK);
443 SetStopExpectations();
444 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
447 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state());
449 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
452 // Start the DTC then block its backend tasks. While its backend
453 // tasks are blocked, stop and start it again, then unblock its
454 // backend tasks. The (delayed) running of the backend tasks from the
455 // stop after the restart shouldn't cause any problems.
456 TEST_F(SyncNonUIDataTypeControllerTest, StopStart) {
457 SetStartExpectations();
458 SetAssociateExpectations();
459 SetActivateExpectations(DataTypeController::OK);
460 SetStopExpectations();
461 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
464 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state());
466 non_ui_dtc_->BlockBackendTasks();
468 SetStartExpectations();
469 SetAssociateExpectations();
470 SetActivateExpectations(DataTypeController::OK);
471 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
473 non_ui_dtc_->UnblockBackendTasks();
476 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state());
479 TEST_F(SyncNonUIDataTypeControllerTest, OnSingleDataTypeUnrecoverableError) {
480 SetStartExpectations();
481 SetAssociateExpectations();
482 SetActivateExpectations(DataTypeController::OK);
483 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state());
486 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state());
488 testing::Mock::VerifyAndClearExpectations(&start_callback_);
489 EXPECT_CALL(model_load_callback_, Run(_, _));
490 syncer::SyncError error(FROM_HERE,
491 syncer::SyncError::DATATYPE_ERROR,
493 non_ui_dtc_->type());
494 backend_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
495 &NonUIDataTypeControllerFake::
496 OnSingleDataTypeUnrecoverableError,
504 } // namespace sync_driver