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