OLD | NEW |
| (Empty) |
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 <utility> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/bind_helpers.h" | |
12 #include "base/callback.h" | |
13 #include "base/location.h" | |
14 #include "base/macros.h" | |
15 #include "base/run_loop.h" | |
16 #include "base/single_thread_task_runner.h" | |
17 #include "base/synchronization/waitable_event.h" | |
18 #include "base/test/test_timeouts.h" | |
19 #include "base/threading/thread.h" | |
20 #include "base/threading/thread_task_runner_handle.h" | |
21 #include "base/tracked_objects.h" | |
22 #include "components/sync/driver/data_type_controller_mock.h" | |
23 #include "components/sync/driver/fake_sync_client.h" | |
24 #include "components/sync/driver/generic_change_processor_factory.h" | |
25 #include "components/sync/driver/non_ui_data_type_controller_mock.h" | |
26 #include "components/sync/engine/model_safe_worker.h" | |
27 #include "components/sync/model/fake_syncable_service.h" | |
28 #include "components/sync/model/sync_change.h" | |
29 #include "testing/gmock/include/gmock/gmock.h" | |
30 #include "testing/gtest/include/gtest/gtest.h" | |
31 | |
32 namespace syncer { | |
33 | |
34 class SyncClient; | |
35 | |
36 namespace { | |
37 | |
38 using base::WaitableEvent; | |
39 using testing::_; | |
40 using testing::AtLeast; | |
41 using testing::DoAll; | |
42 using testing::InvokeWithoutArgs; | |
43 using testing::Mock; | |
44 using testing::Return; | |
45 using testing::SetArgumentPointee; | |
46 using testing::StrictMock; | |
47 | |
48 const ModelType kType = AUTOFILL_PROFILE; | |
49 | |
50 ACTION_P(WaitOnEvent, event) { | |
51 event->Wait(); | |
52 } | |
53 | |
54 ACTION_P(SignalEvent, event) { | |
55 event->Signal(); | |
56 } | |
57 | |
58 ACTION_P(SaveChangeProcessor, scoped_change_processor) { | |
59 scoped_change_processor->reset(arg2); | |
60 } | |
61 | |
62 class SharedChangeProcessorMock : public SharedChangeProcessor { | |
63 public: | |
64 explicit SharedChangeProcessorMock(ModelType type) | |
65 : SharedChangeProcessor(type) {} | |
66 | |
67 base::WeakPtr<SyncableService> Connect( | |
68 SyncClient*, | |
69 GenericChangeProcessorFactory*, | |
70 UserShare*, | |
71 std::unique_ptr<DataTypeErrorHandler>, | |
72 const base::WeakPtr<SyncMergeResult>&) { | |
73 return std::move(connect_return_); | |
74 } | |
75 MOCK_METHOD0(Disconnect, bool()); | |
76 MOCK_METHOD2(ProcessSyncChanges, | |
77 SyncError(const tracked_objects::Location&, | |
78 const SyncChangeList&)); | |
79 MOCK_CONST_METHOD2(GetAllSyncDataReturnError, | |
80 SyncError(ModelType, SyncDataList*)); | |
81 MOCK_METHOD0(GetSyncCount, int()); | |
82 MOCK_METHOD1(SyncModelHasUserCreatedNodes, bool(bool*)); | |
83 MOCK_METHOD0(CryptoReadyIfNecessary, bool()); | |
84 MOCK_CONST_METHOD1(GetDataTypeContext, bool(std::string*)); | |
85 MOCK_METHOD1(RecordAssociationTime, void(base::TimeDelta time)); | |
86 | |
87 void SetConnectReturn(base::WeakPtr<SyncableService> service) { | |
88 connect_return_ = service; | |
89 } | |
90 | |
91 protected: | |
92 virtual ~SharedChangeProcessorMock() { DCHECK(!connect_return_); } | |
93 MOCK_METHOD2(OnUnrecoverableError, | |
94 void(const tracked_objects::Location&, const std::string&)); | |
95 | |
96 private: | |
97 base::WeakPtr<SyncableService> connect_return_; | |
98 DISALLOW_COPY_AND_ASSIGN(SharedChangeProcessorMock); | |
99 }; | |
100 | |
101 class NonUIDataTypeControllerFake : public NonUIDataTypeController { | |
102 public: | |
103 NonUIDataTypeControllerFake( | |
104 SyncClient* sync_client, | |
105 NonUIDataTypeControllerMock* mock, | |
106 SharedChangeProcessor* change_processor, | |
107 scoped_refptr<base::SingleThreadTaskRunner> backend_task_runner) | |
108 : NonUIDataTypeController(kType, | |
109 base::Closure(), | |
110 sync_client, | |
111 GROUP_DB, | |
112 nullptr), | |
113 blocked_(false), | |
114 mock_(mock), | |
115 change_processor_(change_processor), | |
116 backend_task_runner_(backend_task_runner) {} | |
117 ~NonUIDataTypeControllerFake() override {} | |
118 | |
119 // Prevent tasks from being posted on the backend thread until | |
120 // UnblockBackendTasks() is called. | |
121 void BlockBackendTasks() { blocked_ = true; } | |
122 | |
123 // Post pending tasks on the backend thread and start allowing tasks | |
124 // to be posted on the backend thread again. | |
125 void UnblockBackendTasks() { | |
126 blocked_ = false; | |
127 for (std::vector<PendingTask>::const_iterator it = pending_tasks_.begin(); | |
128 it != pending_tasks_.end(); ++it) { | |
129 PostTaskOnModelThread(it->from_here, it->task); | |
130 } | |
131 pending_tasks_.clear(); | |
132 } | |
133 | |
134 SharedChangeProcessor* CreateSharedChangeProcessor() override { | |
135 return change_processor_.get(); | |
136 } | |
137 | |
138 std::unique_ptr<DataTypeErrorHandler> CreateErrorHandler() override { | |
139 return NonUIDataTypeController::CreateErrorHandler(); | |
140 } | |
141 | |
142 protected: | |
143 bool PostTaskOnModelThread(const tracked_objects::Location& from_here, | |
144 const base::Closure& task) override { | |
145 if (blocked_) { | |
146 pending_tasks_.push_back(PendingTask(from_here, task)); | |
147 return true; | |
148 } else { | |
149 return backend_task_runner_->PostTask(from_here, task); | |
150 } | |
151 } | |
152 | |
153 // We mock the following methods because their default implementations do | |
154 // nothing, but we still want to make sure they're called appropriately. | |
155 bool StartModels() override { return mock_->StartModels(); } | |
156 void StopModels() override { mock_->StopModels(); } | |
157 void RecordStartFailure(DataTypeController::ConfigureResult result) override { | |
158 mock_->RecordStartFailure(result); | |
159 } | |
160 | |
161 private: | |
162 struct PendingTask { | |
163 PendingTask(const tracked_objects::Location& from_here, | |
164 const base::Closure& task) | |
165 : from_here(from_here), task(task) {} | |
166 | |
167 tracked_objects::Location from_here; | |
168 base::Closure task; | |
169 }; | |
170 | |
171 bool blocked_; | |
172 std::vector<PendingTask> pending_tasks_; | |
173 NonUIDataTypeControllerMock* mock_; | |
174 scoped_refptr<SharedChangeProcessor> change_processor_; | |
175 scoped_refptr<base::SingleThreadTaskRunner> backend_task_runner_; | |
176 | |
177 DISALLOW_COPY_AND_ASSIGN(NonUIDataTypeControllerFake); | |
178 }; | |
179 | |
180 class SyncNonUIDataTypeControllerTest : public testing::Test, | |
181 public FakeSyncClient { | |
182 public: | |
183 SyncNonUIDataTypeControllerTest() : backend_thread_("dbthread") {} | |
184 | |
185 void SetUp() override { | |
186 backend_thread_.Start(); | |
187 change_processor_ = new SharedChangeProcessorMock(kType); | |
188 // All of these are refcounted, so don't need to be released. | |
189 dtc_mock_ = base::MakeUnique<StrictMock<NonUIDataTypeControllerMock>>(); | |
190 non_ui_dtc_ = base::MakeUnique<NonUIDataTypeControllerFake>( | |
191 this, dtc_mock_.get(), change_processor_.get(), | |
192 backend_thread_.task_runner()); | |
193 } | |
194 | |
195 void TearDown() override { backend_thread_.Stop(); } | |
196 | |
197 void WaitForDTC() { | |
198 WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL, | |
199 base::WaitableEvent::InitialState::NOT_SIGNALED); | |
200 backend_thread_.task_runner()->PostTask( | |
201 FROM_HERE, | |
202 base::Bind(&SyncNonUIDataTypeControllerTest::SignalDone, &done)); | |
203 done.TimedWait(TestTimeouts::action_timeout()); | |
204 if (!done.IsSignaled()) { | |
205 ADD_FAILURE() << "Timed out waiting for DB thread to finish."; | |
206 } | |
207 base::RunLoop().RunUntilIdle(); | |
208 } | |
209 | |
210 SyncService* GetSyncService() override { | |
211 // Make sure this isn't called on backend_thread. | |
212 EXPECT_FALSE(backend_thread_.task_runner()->BelongsToCurrentThread()); | |
213 return FakeSyncClient::GetSyncService(); | |
214 } | |
215 | |
216 protected: | |
217 void SetStartExpectations() { | |
218 EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(true)); | |
219 EXPECT_CALL(model_load_callback_, Run(_, _)); | |
220 } | |
221 | |
222 void SetAssociateExpectations() { | |
223 change_processor_->SetConnectReturn(syncable_service_.AsWeakPtr()); | |
224 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) | |
225 .WillOnce(Return(true)); | |
226 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_)) | |
227 .WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true))); | |
228 EXPECT_CALL(*change_processor_.get(), GetAllSyncDataReturnError(_, _)) | |
229 .WillOnce(Return(SyncError())); | |
230 EXPECT_CALL(*change_processor_.get(), GetSyncCount()).WillOnce(Return(0)); | |
231 EXPECT_CALL(*change_processor_.get(), RecordAssociationTime(_)); | |
232 } | |
233 | |
234 void SetActivateExpectations(DataTypeController::ConfigureResult result) { | |
235 EXPECT_CALL(start_callback_, Run(result, _, _)); | |
236 } | |
237 | |
238 void SetStopExpectations() { | |
239 EXPECT_CALL(*dtc_mock_.get(), StopModels()); | |
240 EXPECT_CALL(*change_processor_.get(), Disconnect()).WillOnce(Return(true)); | |
241 } | |
242 | |
243 void SetStartFailExpectations(DataTypeController::ConfigureResult result) { | |
244 EXPECT_CALL(*dtc_mock_.get(), StopModels()).Times(AtLeast(1)); | |
245 EXPECT_CALL(*dtc_mock_.get(), RecordStartFailure(result)); | |
246 EXPECT_CALL(start_callback_, Run(result, _, _)); | |
247 } | |
248 | |
249 void Start() { | |
250 non_ui_dtc_->LoadModels(base::Bind( | |
251 &ModelLoadCallbackMock::Run, base::Unretained(&model_load_callback_))); | |
252 non_ui_dtc_->StartAssociating(base::Bind( | |
253 &StartCallbackMock::Run, base::Unretained(&start_callback_))); | |
254 } | |
255 | |
256 static void SignalDone(WaitableEvent* done) { done->Signal(); } | |
257 | |
258 base::MessageLoopForUI message_loop_; | |
259 base::Thread backend_thread_; | |
260 | |
261 StartCallbackMock start_callback_; | |
262 ModelLoadCallbackMock model_load_callback_; | |
263 // Must be destroyed after non_ui_dtc_. | |
264 FakeSyncableService syncable_service_; | |
265 std::unique_ptr<NonUIDataTypeControllerFake> non_ui_dtc_; | |
266 std::unique_ptr<NonUIDataTypeControllerMock> dtc_mock_; | |
267 scoped_refptr<SharedChangeProcessorMock> change_processor_; | |
268 std::unique_ptr<SyncChangeProcessor> saved_change_processor_; | |
269 }; | |
270 | |
271 TEST_F(SyncNonUIDataTypeControllerTest, StartOk) { | |
272 SetStartExpectations(); | |
273 SetAssociateExpectations(); | |
274 SetActivateExpectations(DataTypeController::OK); | |
275 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
276 Start(); | |
277 WaitForDTC(); | |
278 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); | |
279 } | |
280 | |
281 TEST_F(SyncNonUIDataTypeControllerTest, StartFirstRun) { | |
282 SetStartExpectations(); | |
283 change_processor_->SetConnectReturn(syncable_service_.AsWeakPtr()); | |
284 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) | |
285 .WillOnce(Return(true)); | |
286 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_)) | |
287 .WillOnce(DoAll(SetArgumentPointee<0>(false), Return(true))); | |
288 EXPECT_CALL(*change_processor_.get(), GetAllSyncDataReturnError(_, _)) | |
289 .WillOnce(Return(SyncError())); | |
290 EXPECT_CALL(*change_processor_.get(), RecordAssociationTime(_)); | |
291 SetActivateExpectations(DataTypeController::OK_FIRST_RUN); | |
292 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
293 Start(); | |
294 WaitForDTC(); | |
295 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); | |
296 } | |
297 | |
298 // Start the DTC and have StartModels() return false. Then, stop the | |
299 // DTC without finishing model startup. It should stop cleanly. | |
300 TEST_F(SyncNonUIDataTypeControllerTest, AbortDuringStartModels) { | |
301 EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(false)); | |
302 EXPECT_CALL(*dtc_mock_.get(), StopModels()); | |
303 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
304 non_ui_dtc_->LoadModels(base::Bind(&ModelLoadCallbackMock::Run, | |
305 base::Unretained(&model_load_callback_))); | |
306 WaitForDTC(); | |
307 EXPECT_EQ(DataTypeController::MODEL_STARTING, non_ui_dtc_->state()); | |
308 non_ui_dtc_->Stop(); | |
309 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
310 } | |
311 | |
312 // Start the DTC and have MergeDataAndStartSyncing() return an error. | |
313 // The DTC should become disabled, and the DTC should still stop | |
314 // cleanly. | |
315 TEST_F(SyncNonUIDataTypeControllerTest, StartAssociationFailed) { | |
316 SetStartExpectations(); | |
317 change_processor_->SetConnectReturn(syncable_service_.AsWeakPtr()); | |
318 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) | |
319 .WillOnce(Return(true)); | |
320 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_)) | |
321 .WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true))); | |
322 EXPECT_CALL(*change_processor_.get(), GetAllSyncDataReturnError(_, _)) | |
323 .WillOnce(Return(SyncError())); | |
324 EXPECT_CALL(*change_processor_.get(), RecordAssociationTime(_)); | |
325 SetStartFailExpectations(DataTypeController::ASSOCIATION_FAILED); | |
326 // Set up association to fail with an association failed error. | |
327 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
328 syncable_service_.set_merge_data_and_start_syncing_error(SyncError( | |
329 FROM_HERE, SyncError::DATATYPE_ERROR, "Sync Error", non_ui_dtc_->type())); | |
330 Start(); | |
331 WaitForDTC(); | |
332 EXPECT_EQ(DataTypeController::DISABLED, non_ui_dtc_->state()); | |
333 non_ui_dtc_->Stop(); | |
334 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
335 } | |
336 | |
337 TEST_F(SyncNonUIDataTypeControllerTest, | |
338 StartAssociationTriggersUnrecoverableError) { | |
339 SetStartExpectations(); | |
340 SetStartFailExpectations(DataTypeController::UNRECOVERABLE_ERROR); | |
341 // Set up association to fail with an unrecoverable error. | |
342 change_processor_->SetConnectReturn(syncable_service_.AsWeakPtr()); | |
343 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) | |
344 .WillRepeatedly(Return(true)); | |
345 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_)) | |
346 .WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false))); | |
347 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
348 Start(); | |
349 WaitForDTC(); | |
350 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
351 } | |
352 | |
353 TEST_F(SyncNonUIDataTypeControllerTest, StartAssociationCryptoNotReady) { | |
354 SetStartExpectations(); | |
355 SetStartFailExpectations(DataTypeController::NEEDS_CRYPTO); | |
356 // Set up association to fail with a NEEDS_CRYPTO error. | |
357 change_processor_->SetConnectReturn(syncable_service_.AsWeakPtr()); | |
358 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) | |
359 .WillRepeatedly(Return(false)); | |
360 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
361 Start(); | |
362 WaitForDTC(); | |
363 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
364 } | |
365 | |
366 // Trigger a Stop() call when we check if the model associator has user created | |
367 // nodes. | |
368 TEST_F(SyncNonUIDataTypeControllerTest, AbortDuringAssociation) { | |
369 WaitableEvent wait_for_db_thread_pause( | |
370 base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
371 base::WaitableEvent::InitialState::NOT_SIGNALED); | |
372 WaitableEvent pause_db_thread( | |
373 base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
374 base::WaitableEvent::InitialState::NOT_SIGNALED); | |
375 | |
376 SetStartExpectations(); | |
377 change_processor_->SetConnectReturn(syncable_service_.AsWeakPtr()); | |
378 EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) | |
379 .WillOnce(Return(true)); | |
380 EXPECT_CALL(*change_processor_.get(), SyncModelHasUserCreatedNodes(_)) | |
381 .WillOnce(DoAll(SignalEvent(&wait_for_db_thread_pause), | |
382 WaitOnEvent(&pause_db_thread), | |
383 SetArgumentPointee<0>(true), Return(true))); | |
384 EXPECT_CALL(*change_processor_.get(), GetAllSyncDataReturnError(_, _)) | |
385 .WillOnce(Return(SyncError(FROM_HERE, SyncError::DATATYPE_ERROR, | |
386 "Disconnected.", kType))); | |
387 EXPECT_CALL(*dtc_mock_.get(), StopModels()); | |
388 EXPECT_CALL(*change_processor_.get(), Disconnect()) | |
389 .WillOnce(DoAll(SignalEvent(&pause_db_thread), Return(true))); | |
390 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
391 Start(); | |
392 wait_for_db_thread_pause.Wait(); | |
393 non_ui_dtc_->Stop(); | |
394 WaitForDTC(); | |
395 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
396 } | |
397 | |
398 // Start the DTC while the backend tasks are blocked. Then stop the DTC before | |
399 // the backend tasks get a chance to run. | |
400 TEST_F(SyncNonUIDataTypeControllerTest, StartAfterSyncShutdown) { | |
401 non_ui_dtc_->BlockBackendTasks(); | |
402 | |
403 SetStartExpectations(); | |
404 // We don't expect StopSyncing to be called because local_service_ will never | |
405 // have been set. | |
406 SetStopExpectations(); | |
407 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
408 Start(); | |
409 non_ui_dtc_->Stop(); | |
410 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
411 Mock::VerifyAndClearExpectations(change_processor_.get()); | |
412 Mock::VerifyAndClearExpectations(dtc_mock_.get()); | |
413 | |
414 non_ui_dtc_->UnblockBackendTasks(); | |
415 WaitForDTC(); | |
416 } | |
417 | |
418 TEST_F(SyncNonUIDataTypeControllerTest, Stop) { | |
419 SetStartExpectations(); | |
420 SetAssociateExpectations(); | |
421 SetActivateExpectations(DataTypeController::OK); | |
422 SetStopExpectations(); | |
423 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
424 Start(); | |
425 WaitForDTC(); | |
426 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); | |
427 non_ui_dtc_->Stop(); | |
428 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
429 } | |
430 | |
431 // Start the DTC then block its backend tasks. While its backend | |
432 // tasks are blocked, stop and start it again, then unblock its | |
433 // backend tasks. The (delayed) running of the backend tasks from the | |
434 // stop after the restart shouldn't cause any problems. | |
435 TEST_F(SyncNonUIDataTypeControllerTest, StopStart) { | |
436 SetStartExpectations(); | |
437 SetAssociateExpectations(); | |
438 SetActivateExpectations(DataTypeController::OK); | |
439 SetStopExpectations(); | |
440 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
441 Start(); | |
442 WaitForDTC(); | |
443 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); | |
444 | |
445 non_ui_dtc_->BlockBackendTasks(); | |
446 non_ui_dtc_->Stop(); | |
447 SetStartExpectations(); | |
448 SetAssociateExpectations(); | |
449 SetActivateExpectations(DataTypeController::OK); | |
450 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
451 Start(); | |
452 non_ui_dtc_->UnblockBackendTasks(); | |
453 | |
454 WaitForDTC(); | |
455 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); | |
456 } | |
457 | |
458 TEST_F(SyncNonUIDataTypeControllerTest, OnUnrecoverableError) { | |
459 SetStartExpectations(); | |
460 SetAssociateExpectations(); | |
461 SetActivateExpectations(DataTypeController::OK); | |
462 EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); | |
463 Start(); | |
464 WaitForDTC(); | |
465 EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); | |
466 | |
467 testing::Mock::VerifyAndClearExpectations(&start_callback_); | |
468 EXPECT_CALL(model_load_callback_, Run(_, _)); | |
469 SyncError error(FROM_HERE, SyncError::DATATYPE_ERROR, "error", | |
470 non_ui_dtc_->type()); | |
471 backend_thread_.task_runner()->PostTask( | |
472 FROM_HERE, | |
473 base::Bind(&DataTypeErrorHandler::OnUnrecoverableError, | |
474 base::Passed(non_ui_dtc_->CreateErrorHandler()), error)); | |
475 WaitForDTC(); | |
476 } | |
477 | |
478 } // namespace | |
479 | |
480 } // namespace syncer | |
OLD | NEW |