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.
8 #include "base/callback.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/sequenced_task_runner.h"
12 #include "base/test/test_simple_task_runner.h"
13 #include "components/sync_driver/non_blocking_data_type_controller.h"
14 #include "sync/engine/model_type_sync_proxy_impl.h"
15 #include "sync/engine/model_type_sync_worker.h"
16 #include "sync/internal_api/public/base/model_type.h"
17 #include "sync/internal_api/public/sync_context_proxy.h"
18 #include "testing/gtest/include/gtest/gtest.h"
20 namespace sync_driver {
22 class ModelTypeSyncWorker;
26 // A useless instance of ModelTypeSyncWorker.
27 class NullModelTypeSyncWorker : public syncer::ModelTypeSyncWorker {
29 NullModelTypeSyncWorker();
30 virtual ~NullModelTypeSyncWorker();
32 virtual void EnqueueForCommit(
33 const syncer::CommitRequestDataList& list) OVERRIDE;
36 NullModelTypeSyncWorker::NullModelTypeSyncWorker() {
39 NullModelTypeSyncWorker::~NullModelTypeSyncWorker() {
42 void NullModelTypeSyncWorker::EnqueueForCommit(
43 const syncer::CommitRequestDataList& list) {
44 NOTREACHED() << "Not implemented.";
47 // A class that pretends to be the sync backend.
48 class MockSyncContext {
51 syncer::ModelType type,
52 const scoped_refptr<base::SingleThreadTaskRunner>& model_task_runner,
53 const base::WeakPtr<syncer::ModelTypeSyncProxyImpl>& type_proxy) {
54 enabled_types_.Put(type);
55 model_task_runner->PostTask(
57 base::Bind(&syncer::ModelTypeSyncProxyImpl::OnConnect,
59 base::Passed(scoped_ptr<syncer::ModelTypeSyncWorker>(
60 new NullModelTypeSyncWorker()).Pass())));
63 void Disconnect(syncer::ModelType type) {
64 DCHECK(enabled_types_.Has(type));
65 enabled_types_.Remove(type);
69 std::list<base::Closure> tasks_;
70 syncer::ModelTypeSet enabled_types_;
73 // A proxy to the MockSyncContext that implements SyncContextProxy.
74 class MockSyncContextProxy : public syncer::SyncContextProxy {
77 MockSyncContext* sync_context,
78 const scoped_refptr<base::TestSimpleTaskRunner>& model_task_runner,
79 const scoped_refptr<base::TestSimpleTaskRunner>& sync_task_runner)
80 : mock_sync_context_(sync_context),
81 model_task_runner_(model_task_runner),
82 sync_task_runner_(sync_task_runner) {}
83 virtual ~MockSyncContextProxy() {}
85 virtual void ConnectTypeToSync(
86 syncer::ModelType type,
87 const syncer::DataTypeState& data_type_state,
88 const syncer::UpdateResponseDataList& saved_pending_updates,
89 const base::WeakPtr<syncer::ModelTypeSyncProxyImpl>& type_proxy)
91 // Normally we'd use MessageLoopProxy::current() as the TaskRunner argument
92 // to Connect(). That won't work here in this test, so we use the
93 // model_task_runner_ that was injected for this purpose instead.
94 sync_task_runner_->PostTask(FROM_HERE,
95 base::Bind(&MockSyncContext::Connect,
96 base::Unretained(mock_sync_context_),
102 virtual void Disconnect(syncer::ModelType type) OVERRIDE {
103 sync_task_runner_->PostTask(FROM_HERE,
104 base::Bind(&MockSyncContext::Disconnect,
105 base::Unretained(mock_sync_context_),
109 virtual scoped_ptr<SyncContextProxy> Clone() const OVERRIDE {
110 return scoped_ptr<SyncContextProxy>(new MockSyncContextProxy(
111 mock_sync_context_, model_task_runner_, sync_task_runner_));
115 MockSyncContext* mock_sync_context_;
116 scoped_refptr<base::TestSimpleTaskRunner> model_task_runner_;
117 scoped_refptr<base::TestSimpleTaskRunner> sync_task_runner_;
122 class NonBlockingDataTypeControllerTest : public testing::Test {
124 NonBlockingDataTypeControllerTest()
125 : type_sync_proxy_(syncer::DICTIONARY),
126 model_thread_(new base::TestSimpleTaskRunner()),
127 sync_thread_(new base::TestSimpleTaskRunner()),
128 controller_(syncer::DICTIONARY, true),
129 mock_context_proxy_(&mock_sync_context_, model_thread_, sync_thread_),
130 auto_run_tasks_(true) {}
132 virtual ~NonBlockingDataTypeControllerTest() {}
134 // Connects the sync type proxy to the NonBlockingDataTypeController.
135 void InitTypeSyncProxy() {
136 controller_.InitializeType(model_thread_,
137 type_sync_proxy_.AsWeakPtrForUI());
138 if (auto_run_tasks_) {
143 // Connects the sync backend to the NonBlockingDataTypeController.
144 void InitSyncBackend() {
145 controller_.InitializeSyncContext(mock_context_proxy_.Clone());
146 if (auto_run_tasks_) {
151 // Disconnects the sync backend from the NonBlockingDataTypeController.
152 void UninitializeSyncBackend() {
153 controller_.ClearSyncContext();
154 if (auto_run_tasks_) {
159 // Toggles the user's preference for syncing this type.
160 void SetIsPreferred(bool preferred) {
161 controller_.SetIsPreferred(preferred);
162 if (auto_run_tasks_) {
167 // These threads can ping-pong for a bit so we run the model thread twice.
169 RunQueuedModelThreadTasks();
170 RunQueuedSyncThreadTasks();
171 RunQueuedModelThreadTasks();
174 // The sync type proxy pretends to run tasks on a different thread.
175 // This function runs any posted tasks.
176 void RunQueuedModelThreadTasks() { model_thread_->RunUntilIdle(); }
178 // Processes any pending connect or disconnect requests and sends
179 // responses synchronously.
180 void RunQueuedSyncThreadTasks() { sync_thread_->RunUntilIdle(); }
182 void SetAutoRunTasks(bool auto_run_tasks) {
183 auto_run_tasks_ = auto_run_tasks;
187 syncer::ModelTypeSyncProxyImpl type_sync_proxy_;
188 scoped_refptr<base::TestSimpleTaskRunner> model_thread_;
189 scoped_refptr<base::TestSimpleTaskRunner> sync_thread_;
191 NonBlockingDataTypeController controller_;
193 MockSyncContext mock_sync_context_;
194 MockSyncContextProxy mock_context_proxy_;
196 bool auto_run_tasks_;
199 // Initialization when the user has disabled syncing for this type.
200 TEST_F(NonBlockingDataTypeControllerTest, UserDisabled) {
201 SetIsPreferred(false);
205 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
206 EXPECT_FALSE(type_sync_proxy_.IsConnected());
208 UninitializeSyncBackend();
210 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
211 EXPECT_FALSE(type_sync_proxy_.IsConnected());
214 // Init the sync backend then the type sync proxy.
215 TEST_F(NonBlockingDataTypeControllerTest, Enabled_SyncFirst) {
216 SetIsPreferred(true);
218 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
219 EXPECT_FALSE(type_sync_proxy_.IsConnected());
222 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
223 EXPECT_TRUE(type_sync_proxy_.IsConnected());
225 UninitializeSyncBackend();
226 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
227 EXPECT_FALSE(type_sync_proxy_.IsConnected());
230 // Init the type sync proxy then the sync backend.
231 TEST_F(NonBlockingDataTypeControllerTest, Enabled_ProcessorFirst) {
232 SetIsPreferred(true);
234 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
235 EXPECT_FALSE(type_sync_proxy_.IsConnected());
238 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
239 EXPECT_TRUE(type_sync_proxy_.IsConnected());
241 UninitializeSyncBackend();
242 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
243 EXPECT_FALSE(type_sync_proxy_.IsConnected());
246 // Initialize sync then disable it with a pref change.
247 TEST_F(NonBlockingDataTypeControllerTest, PreferThenNot) {
248 SetIsPreferred(true);
252 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
253 EXPECT_TRUE(type_sync_proxy_.IsConnected());
255 SetIsPreferred(false);
256 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
257 EXPECT_FALSE(type_sync_proxy_.IsConnected());
260 // Connect type sync proxy and sync backend, then toggle prefs repeatedly.
261 TEST_F(NonBlockingDataTypeControllerTest, RepeatedTogglePreference) {
262 SetIsPreferred(false);
265 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
266 EXPECT_FALSE(type_sync_proxy_.IsConnected());
268 SetIsPreferred(true);
269 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
270 EXPECT_TRUE(type_sync_proxy_.IsConnected());
272 SetIsPreferred(false);
273 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
274 EXPECT_FALSE(type_sync_proxy_.IsConnected());
276 SetIsPreferred(true);
277 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
278 EXPECT_TRUE(type_sync_proxy_.IsConnected());
280 SetIsPreferred(false);
281 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
282 EXPECT_FALSE(type_sync_proxy_.IsConnected());
285 // Test sync backend getting restarted while processor is connected.
286 TEST_F(NonBlockingDataTypeControllerTest, RestartSyncBackend) {
287 SetIsPreferred(true);
290 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
291 EXPECT_TRUE(type_sync_proxy_.IsConnected());
293 // Shutting down sync backend should disconnect but not disable the type.
294 UninitializeSyncBackend();
295 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
296 EXPECT_FALSE(type_sync_proxy_.IsConnected());
298 // Brining the backend back should reconnect the type.
300 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
301 EXPECT_TRUE(type_sync_proxy_.IsConnected());
304 // Test sync backend being restarted before processor connects.
305 TEST_F(NonBlockingDataTypeControllerTest, RestartSyncBackendEarly) {
306 SetIsPreferred(true);
308 // Toggle sync off and on before the type sync proxy is available.
310 EXPECT_FALSE(type_sync_proxy_.IsConnected());
311 UninitializeSyncBackend();
312 EXPECT_FALSE(type_sync_proxy_.IsConnected());
314 EXPECT_FALSE(type_sync_proxy_.IsConnected());
316 // Introduce the processor.
318 EXPECT_TRUE(type_sync_proxy_.IsConnected());
321 // Test pref toggling before the sync backend has connected.
322 TEST_F(NonBlockingDataTypeControllerTest, TogglePreferenceWithoutBackend) {
323 SetIsPreferred(true);
326 // This should emit a disable signal.
327 SetIsPreferred(false);
328 EXPECT_FALSE(type_sync_proxy_.IsConnected());
329 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
331 // This won't enable us, since we don't have a sync backend.
332 SetIsPreferred(true);
333 EXPECT_FALSE(type_sync_proxy_.IsConnected());
334 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
336 // Only now do we start sending enable signals.
338 EXPECT_TRUE(type_sync_proxy_.IsConnected());
339 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
342 // Turns off auto-task-running to test the effects of delaying a connection
345 // This is mostly a test of the test framework. It's not very interesting on
346 // its own, but it provides a useful "control" against some of the more
347 // complicated race tests below.
348 TEST_F(NonBlockingDataTypeControllerTest, DelayedConnect) {
349 SetAutoRunTasks(false);
351 SetIsPreferred(true);
355 // Allow the model to emit the request.
356 RunQueuedModelThreadTasks();
358 // That should result in a request to connect, but it won't be
359 // executed right away.
360 EXPECT_FALSE(type_sync_proxy_.IsConnected());
361 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
363 // Let the sync thread process the request and the model thread handle its
365 RunQueuedSyncThreadTasks();
366 RunQueuedModelThreadTasks();
368 EXPECT_TRUE(type_sync_proxy_.IsConnected());
369 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
372 // Send Disable signal while a connection request is in progress.
373 TEST_F(NonBlockingDataTypeControllerTest, DisableRacesWithOnConnect) {
374 SetAutoRunTasks(false);
376 SetIsPreferred(true);
380 // Allow the model to emit the request.
381 RunQueuedModelThreadTasks();
383 // That should result in a request to connect, but it won't be
384 // executed right away.
385 EXPECT_FALSE(type_sync_proxy_.IsConnected());
386 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
388 // Send and execute a disable signal before the OnConnect callback returns.
389 SetIsPreferred(false);
391 // Now we let sync process the initial request and the disable request,
392 // both of which should be sitting in its queue.
393 RunQueuedSyncThreadTasks();
395 // Let the model thread process any responses received from the sync thread.
396 // A plausible error would be that the sync thread returns a "connection OK"
397 // message, and this message overrides the request to disable that arrived
398 // from the UI thread earlier. We need to make sure that doesn't happen.
399 RunQueuedModelThreadTasks();
401 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
402 EXPECT_FALSE(type_sync_proxy_.IsConnected());
405 // Send a request to enable, then disable, then re-enable the data type.
407 // To make it more interesting, we stall the sync thread until all three
408 // requests have been passed to the model thread.
409 TEST_F(NonBlockingDataTypeControllerTest, EnableDisableEnableRace) {
410 SetAutoRunTasks(false);
412 SetIsPreferred(true);
415 RunQueuedModelThreadTasks();
417 // That was the first enable.
418 EXPECT_FALSE(type_sync_proxy_.IsConnected());
419 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
422 SetIsPreferred(false);
423 RunQueuedModelThreadTasks();
424 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
427 SetIsPreferred(true);
428 RunQueuedModelThreadTasks();
429 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
431 // The sync thread has three messages related to those enables and
432 // disables sittin in its queue. Let's allow it to process them.
433 RunQueuedSyncThreadTasks();
435 // Let the model thread process any messages from the sync thread.
436 RunQueuedModelThreadTasks();
437 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
438 EXPECT_TRUE(type_sync_proxy_.IsConnected());
441 } // namespace sync_driver