Home | History | Annotate | Download | only in sync_driver
      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 
     26 namespace sync_driver {
     27 
     28 namespace {
     29 
     30 using base::WaitableEvent;
     31 using syncer::AUTOFILL_PROFILE;
     32 using testing::_;
     33 using testing::AtLeast;
     34 using testing::DoAll;
     35 using testing::InvokeWithoutArgs;
     36 using testing::Mock;
     37 using testing::Return;
     38 using testing::SetArgumentPointee;
     39 using testing::StrictMock;
     40 
     41 ACTION_P(WaitOnEvent, event) {
     42   event->Wait();
     43 }
     44 
     45 ACTION_P(SignalEvent, event) {
     46   event->Signal();
     47 }
     48 
     49 ACTION_P(SaveChangeProcessor, scoped_change_processor) {
     50   scoped_change_processor->reset(arg2);
     51 }
     52 
     53 ACTION_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 
     59 class 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 
     92 class 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 
    186 class 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 
    280 TEST_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 
    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());
    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.
    310 TEST_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.
    326 TEST_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 
    352 TEST_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 
    369 TEST_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.
    386 TEST_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.
    418 TEST_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 
    438 TEST_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.
    455 TEST_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 
    478 TEST_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