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 ~NullModelTypeSyncWorker() override;
32 void EnqueueForCommit(const syncer::CommitRequestDataList& list) override;
35 NullModelTypeSyncWorker::NullModelTypeSyncWorker() {
38 NullModelTypeSyncWorker::~NullModelTypeSyncWorker() {
41 void NullModelTypeSyncWorker::EnqueueForCommit(
42 const syncer::CommitRequestDataList& list) {
43 NOTREACHED() << "Not implemented.";
46 // A class that pretends to be the sync backend.
47 class MockSyncContext {
50 syncer::ModelType type,
51 const scoped_refptr<base::SingleThreadTaskRunner>& model_task_runner,
52 const base::WeakPtr<syncer::ModelTypeSyncProxyImpl>& type_proxy) {
53 enabled_types_.Put(type);
54 model_task_runner->PostTask(
56 base::Bind(&syncer::ModelTypeSyncProxyImpl::OnConnect,
58 base::Passed(scoped_ptr<syncer::ModelTypeSyncWorker>(
59 new NullModelTypeSyncWorker()).Pass())));
62 void Disconnect(syncer::ModelType type) {
63 DCHECK(enabled_types_.Has(type));
64 enabled_types_.Remove(type);
68 std::list<base::Closure> tasks_;
69 syncer::ModelTypeSet enabled_types_;
72 // A proxy to the MockSyncContext that implements SyncContextProxy.
73 class MockSyncContextProxy : public syncer::SyncContextProxy {
76 MockSyncContext* sync_context,
77 const scoped_refptr<base::TestSimpleTaskRunner>& model_task_runner,
78 const scoped_refptr<base::TestSimpleTaskRunner>& sync_task_runner)
79 : mock_sync_context_(sync_context),
80 model_task_runner_(model_task_runner),
81 sync_task_runner_(sync_task_runner) {}
82 ~MockSyncContextProxy() override {}
84 void ConnectTypeToSync(
85 syncer::ModelType type,
86 const syncer::DataTypeState& data_type_state,
87 const syncer::UpdateResponseDataList& saved_pending_updates,
88 const base::WeakPtr<syncer::ModelTypeSyncProxyImpl>& type_proxy)
90 // Normally we'd use MessageLoopProxy::current() as the TaskRunner argument
91 // to Connect(). That won't work here in this test, so we use the
92 // model_task_runner_ that was injected for this purpose instead.
93 sync_task_runner_->PostTask(FROM_HERE,
94 base::Bind(&MockSyncContext::Connect,
95 base::Unretained(mock_sync_context_),
101 void Disconnect(syncer::ModelType type) override {
102 sync_task_runner_->PostTask(FROM_HERE,
103 base::Bind(&MockSyncContext::Disconnect,
104 base::Unretained(mock_sync_context_),
108 scoped_ptr<SyncContextProxy> Clone() const override {
109 return scoped_ptr<SyncContextProxy>(new MockSyncContextProxy(
110 mock_sync_context_, model_task_runner_, sync_task_runner_));
114 MockSyncContext* mock_sync_context_;
115 scoped_refptr<base::TestSimpleTaskRunner> model_task_runner_;
116 scoped_refptr<base::TestSimpleTaskRunner> sync_task_runner_;
121 class NonBlockingDataTypeControllerTest : public testing::Test {
123 NonBlockingDataTypeControllerTest()
124 : type_sync_proxy_(syncer::DICTIONARY),
125 model_thread_(new base::TestSimpleTaskRunner()),
126 sync_thread_(new base::TestSimpleTaskRunner()),
127 controller_(syncer::DICTIONARY, true),
128 mock_context_proxy_(&mock_sync_context_, model_thread_, sync_thread_),
129 auto_run_tasks_(true) {}
131 ~NonBlockingDataTypeControllerTest() override {}
133 // Connects the sync type proxy to the NonBlockingDataTypeController.
134 void InitTypeSyncProxy() {
135 controller_.InitializeType(model_thread_,
136 type_sync_proxy_.AsWeakPtrForUI());
137 if (auto_run_tasks_) {
142 // Connects the sync backend to the NonBlockingDataTypeController.
143 void InitSyncBackend() {
144 controller_.InitializeSyncContext(mock_context_proxy_.Clone());
145 if (auto_run_tasks_) {
150 // Disconnects the sync backend from the NonBlockingDataTypeController.
151 void UninitializeSyncBackend() {
152 controller_.ClearSyncContext();
153 if (auto_run_tasks_) {
158 // Toggles the user's preference for syncing this type.
159 void SetIsPreferred(bool preferred) {
160 controller_.SetIsPreferred(preferred);
161 if (auto_run_tasks_) {
166 // These threads can ping-pong for a bit so we run the model thread twice.
168 RunQueuedModelThreadTasks();
169 RunQueuedSyncThreadTasks();
170 RunQueuedModelThreadTasks();
173 // The sync type proxy pretends to run tasks on a different thread.
174 // This function runs any posted tasks.
175 void RunQueuedModelThreadTasks() { model_thread_->RunUntilIdle(); }
177 // Processes any pending connect or disconnect requests and sends
178 // responses synchronously.
179 void RunQueuedSyncThreadTasks() { sync_thread_->RunUntilIdle(); }
181 void SetAutoRunTasks(bool auto_run_tasks) {
182 auto_run_tasks_ = auto_run_tasks;
186 syncer::ModelTypeSyncProxyImpl type_sync_proxy_;
187 scoped_refptr<base::TestSimpleTaskRunner> model_thread_;
188 scoped_refptr<base::TestSimpleTaskRunner> sync_thread_;
190 NonBlockingDataTypeController controller_;
192 MockSyncContext mock_sync_context_;
193 MockSyncContextProxy mock_context_proxy_;
195 bool auto_run_tasks_;
198 // Initialization when the user has disabled syncing for this type.
199 TEST_F(NonBlockingDataTypeControllerTest, UserDisabled) {
200 SetIsPreferred(false);
204 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
205 EXPECT_FALSE(type_sync_proxy_.IsConnected());
207 UninitializeSyncBackend();
209 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
210 EXPECT_FALSE(type_sync_proxy_.IsConnected());
213 // Init the sync backend then the type sync proxy.
214 TEST_F(NonBlockingDataTypeControllerTest, Enabled_SyncFirst) {
215 SetIsPreferred(true);
217 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
218 EXPECT_FALSE(type_sync_proxy_.IsConnected());
221 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
222 EXPECT_TRUE(type_sync_proxy_.IsConnected());
224 UninitializeSyncBackend();
225 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
226 EXPECT_FALSE(type_sync_proxy_.IsConnected());
229 // Init the type sync proxy then the sync backend.
230 TEST_F(NonBlockingDataTypeControllerTest, Enabled_ProcessorFirst) {
231 SetIsPreferred(true);
233 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
234 EXPECT_FALSE(type_sync_proxy_.IsConnected());
237 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
238 EXPECT_TRUE(type_sync_proxy_.IsConnected());
240 UninitializeSyncBackend();
241 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
242 EXPECT_FALSE(type_sync_proxy_.IsConnected());
245 // Initialize sync then disable it with a pref change.
246 TEST_F(NonBlockingDataTypeControllerTest, PreferThenNot) {
247 SetIsPreferred(true);
251 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
252 EXPECT_TRUE(type_sync_proxy_.IsConnected());
254 SetIsPreferred(false);
255 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
256 EXPECT_FALSE(type_sync_proxy_.IsConnected());
259 // Connect type sync proxy and sync backend, then toggle prefs repeatedly.
260 TEST_F(NonBlockingDataTypeControllerTest, RepeatedTogglePreference) {
261 SetIsPreferred(false);
264 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
265 EXPECT_FALSE(type_sync_proxy_.IsConnected());
267 SetIsPreferred(true);
268 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
269 EXPECT_TRUE(type_sync_proxy_.IsConnected());
271 SetIsPreferred(false);
272 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
273 EXPECT_FALSE(type_sync_proxy_.IsConnected());
275 SetIsPreferred(true);
276 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
277 EXPECT_TRUE(type_sync_proxy_.IsConnected());
279 SetIsPreferred(false);
280 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
281 EXPECT_FALSE(type_sync_proxy_.IsConnected());
284 // Test sync backend getting restarted while processor is connected.
285 TEST_F(NonBlockingDataTypeControllerTest, RestartSyncBackend) {
286 SetIsPreferred(true);
289 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
290 EXPECT_TRUE(type_sync_proxy_.IsConnected());
292 // Shutting down sync backend should disconnect but not disable the type.
293 UninitializeSyncBackend();
294 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
295 EXPECT_FALSE(type_sync_proxy_.IsConnected());
297 // Brining the backend back should reconnect the type.
299 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
300 EXPECT_TRUE(type_sync_proxy_.IsConnected());
303 // Test sync backend being restarted before processor connects.
304 TEST_F(NonBlockingDataTypeControllerTest, RestartSyncBackendEarly) {
305 SetIsPreferred(true);
307 // Toggle sync off and on before the type sync proxy is available.
309 EXPECT_FALSE(type_sync_proxy_.IsConnected());
310 UninitializeSyncBackend();
311 EXPECT_FALSE(type_sync_proxy_.IsConnected());
313 EXPECT_FALSE(type_sync_proxy_.IsConnected());
315 // Introduce the processor.
317 EXPECT_TRUE(type_sync_proxy_.IsConnected());
320 // Test pref toggling before the sync backend has connected.
321 TEST_F(NonBlockingDataTypeControllerTest, TogglePreferenceWithoutBackend) {
322 SetIsPreferred(true);
325 // This should emit a disable signal.
326 SetIsPreferred(false);
327 EXPECT_FALSE(type_sync_proxy_.IsConnected());
328 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
330 // This won't enable us, since we don't have a sync backend.
331 SetIsPreferred(true);
332 EXPECT_FALSE(type_sync_proxy_.IsConnected());
333 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
335 // Only now do we start sending enable signals.
337 EXPECT_TRUE(type_sync_proxy_.IsConnected());
338 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
341 // Turns off auto-task-running to test the effects of delaying a connection
344 // This is mostly a test of the test framework. It's not very interesting on
345 // its own, but it provides a useful "control" against some of the more
346 // complicated race tests below.
347 TEST_F(NonBlockingDataTypeControllerTest, DelayedConnect) {
348 SetAutoRunTasks(false);
350 SetIsPreferred(true);
354 // Allow the model to emit the request.
355 RunQueuedModelThreadTasks();
357 // That should result in a request to connect, but it won't be
358 // executed right away.
359 EXPECT_FALSE(type_sync_proxy_.IsConnected());
360 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
362 // Let the sync thread process the request and the model thread handle its
364 RunQueuedSyncThreadTasks();
365 RunQueuedModelThreadTasks();
367 EXPECT_TRUE(type_sync_proxy_.IsConnected());
368 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
371 // Send Disable signal while a connection request is in progress.
372 TEST_F(NonBlockingDataTypeControllerTest, DisableRacesWithOnConnect) {
373 SetAutoRunTasks(false);
375 SetIsPreferred(true);
379 // Allow the model to emit the request.
380 RunQueuedModelThreadTasks();
382 // That should result in a request to connect, but it won't be
383 // executed right away.
384 EXPECT_FALSE(type_sync_proxy_.IsConnected());
385 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
387 // Send and execute a disable signal before the OnConnect callback returns.
388 SetIsPreferred(false);
390 // Now we let sync process the initial request and the disable request,
391 // both of which should be sitting in its queue.
392 RunQueuedSyncThreadTasks();
394 // Let the model thread process any responses received from the sync thread.
395 // A plausible error would be that the sync thread returns a "connection OK"
396 // message, and this message overrides the request to disable that arrived
397 // from the UI thread earlier. We need to make sure that doesn't happen.
398 RunQueuedModelThreadTasks();
400 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
401 EXPECT_FALSE(type_sync_proxy_.IsConnected());
404 // Send a request to enable, then disable, then re-enable the data type.
406 // To make it more interesting, we stall the sync thread until all three
407 // requests have been passed to the model thread.
408 TEST_F(NonBlockingDataTypeControllerTest, EnableDisableEnableRace) {
409 SetAutoRunTasks(false);
411 SetIsPreferred(true);
414 RunQueuedModelThreadTasks();
416 // That was the first enable.
417 EXPECT_FALSE(type_sync_proxy_.IsConnected());
418 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
421 SetIsPreferred(false);
422 RunQueuedModelThreadTasks();
423 EXPECT_FALSE(type_sync_proxy_.IsPreferred());
426 SetIsPreferred(true);
427 RunQueuedModelThreadTasks();
428 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
430 // The sync thread has three messages related to those enables and
431 // disables sittin in its queue. Let's allow it to process them.
432 RunQueuedSyncThreadTasks();
434 // Let the model thread process any messages from the sync thread.
435 RunQueuedModelThreadTasks();
436 EXPECT_TRUE(type_sync_proxy_.IsPreferred());
437 EXPECT_TRUE(type_sync_proxy_.IsConnected());
440 } // namespace sync_driver