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