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/sync_backend_registrar.h"
6
7#include "base/run_loop.h"
8#include "chrome/browser/sync/glue/ui_model_worker.h"
9#include "chrome/test/base/testing_profile.h"
10#include "components/sync_driver/change_processor_mock.h"
11#include "content/public/browser/browser_thread.h"
12#include "content/public/test/test_browser_thread_bundle.h"
13#include "sync/internal_api/public/base/model_type.h"
14#include "sync/internal_api/public/test/test_user_share.h"
15#include "testing/gmock/include/gmock/gmock.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace browser_sync {
19
20namespace {
21
22using ::testing::_;
23using ::testing::InSequence;
24using ::testing::Return;
25using ::testing::StrictMock;
26using content::BrowserThread;
27using syncer::FIRST_REAL_MODEL_TYPE;
28using syncer::AUTOFILL;
29using syncer::BOOKMARKS;
30using syncer::PREFERENCES;
31using syncer::THEMES;
32using syncer::NIGORI;
33using syncer::PASSWORDS;
34using syncer::MODEL_TYPE_COUNT;
35using syncer::ModelTypeSet;
36using syncer::ModelType;
37using syncer::ModelTypeFromInt;
38
39void TriggerChanges(SyncBackendRegistrar* registrar, ModelType type) {
40  registrar->OnChangesApplied(type, 0, NULL,
41                              syncer::ImmutableChangeRecordList());
42  registrar->OnChangesComplete(type);
43}
44
45class SyncBackendRegistrarTest : public testing::Test {
46 public:
47  void TestNonUIDataTypeActivationAsync(sync_driver::ChangeProcessor* processor,
48                                        base::WaitableEvent* done) {
49    registrar_->ActivateDataType(AUTOFILL,
50                                 syncer::GROUP_DB,
51                                 processor,
52                                 test_user_share_.user_share());
53    syncer::ModelSafeRoutingInfo expected_routing_info;
54    expected_routing_info[AUTOFILL] = syncer::GROUP_DB;
55    ExpectRoutingInfo(registrar_.get(), expected_routing_info);
56    ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet(AUTOFILL));
57    TriggerChanges(registrar_.get(), AUTOFILL);
58    done->Signal();
59  }
60
61 protected:
62  SyncBackendRegistrarTest()
63      : sync_thread_(NULL),
64        thread_bundle_(content::TestBrowserThreadBundle::REAL_DB_THREAD |
65                       content::TestBrowserThreadBundle::REAL_FILE_THREAD |
66                       content::TestBrowserThreadBundle::REAL_IO_THREAD) {}
67
68  virtual ~SyncBackendRegistrarTest() {}
69
70  virtual void SetUp() {
71    test_user_share_.SetUp();
72    registrar_.reset(new SyncBackendRegistrar("test", &profile_,
73                                              scoped_ptr<base::Thread>()));
74    sync_thread_ = registrar_->sync_thread();
75  }
76
77  virtual void TearDown() {
78    registrar_->RequestWorkerStopOnUIThread();
79    test_user_share_.TearDown();
80    sync_thread_->message_loop()->PostTask(
81        FROM_HERE,
82        base::Bind(&SyncBackendRegistrar::Shutdown,
83                   base::Unretained(registrar_.release())));
84    sync_thread_->message_loop()->RunUntilIdle();
85  }
86
87  void ExpectRoutingInfo(
88      SyncBackendRegistrar* registrar,
89      const syncer::ModelSafeRoutingInfo& expected_routing_info) {
90    syncer::ModelSafeRoutingInfo routing_info;
91    registrar->GetModelSafeRoutingInfo(&routing_info);
92    EXPECT_EQ(expected_routing_info, routing_info);
93  }
94
95  void ExpectHasProcessorsForTypes(const SyncBackendRegistrar& registrar,
96                                   ModelTypeSet types) {
97    for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
98      ModelType model_type = ModelTypeFromInt(i);
99      EXPECT_EQ(types.Has(model_type),
100                registrar_->IsTypeActivatedForTest(model_type));
101    }
102  }
103
104  syncer::TestUserShare test_user_share_;
105  TestingProfile profile_;
106  scoped_ptr<SyncBackendRegistrar> registrar_;
107
108  base::Thread* sync_thread_;
109  content::TestBrowserThreadBundle thread_bundle_;
110};
111
112TEST_F(SyncBackendRegistrarTest, ConstructorEmpty) {
113  registrar_->SetInitialTypes(ModelTypeSet());
114  EXPECT_FALSE(registrar_->IsNigoriEnabled());
115  {
116    std::vector<scoped_refptr<syncer::ModelSafeWorker> > workers;
117    registrar_->GetWorkers(&workers);
118    EXPECT_EQ(4u, workers.size());
119  }
120  ExpectRoutingInfo(registrar_.get(), syncer::ModelSafeRoutingInfo());
121  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
122}
123
124TEST_F(SyncBackendRegistrarTest, ConstructorNonEmpty) {
125  const ModelTypeSet initial_types(BOOKMARKS, NIGORI, PASSWORDS);
126  registrar_->SetInitialTypes(initial_types);
127  EXPECT_TRUE(registrar_->IsNigoriEnabled());
128  {
129    std::vector<scoped_refptr<syncer::ModelSafeWorker> > workers;
130    registrar_->GetWorkers(&workers);
131    EXPECT_EQ(4u, workers.size());
132  }
133  {
134    syncer::ModelSafeRoutingInfo expected_routing_info;
135    expected_routing_info[BOOKMARKS] = syncer::GROUP_PASSIVE;
136    expected_routing_info[NIGORI] = syncer::GROUP_PASSIVE;
137    // Passwords dropped because of no password store.
138    ExpectRoutingInfo(registrar_.get(), expected_routing_info);
139  }
140  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
141}
142
143TEST_F(SyncBackendRegistrarTest, ConfigureDataTypes) {
144  registrar_->SetInitialTypes(ModelTypeSet());
145
146  // Add.
147  const ModelTypeSet types1(BOOKMARKS, NIGORI, AUTOFILL);
148  EXPECT_TRUE(
149      registrar_->ConfigureDataTypes(types1, ModelTypeSet()).Equals(types1));
150  {
151    syncer::ModelSafeRoutingInfo expected_routing_info;
152    expected_routing_info[BOOKMARKS] = syncer::GROUP_PASSIVE;
153    expected_routing_info[NIGORI] = syncer::GROUP_PASSIVE;
154    expected_routing_info[AUTOFILL] = syncer::GROUP_PASSIVE;
155    ExpectRoutingInfo(registrar_.get(), expected_routing_info);
156  }
157  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
158  EXPECT_TRUE(types1.Equals(registrar_->GetLastConfiguredTypes()));
159
160  // Add and remove.
161  const ModelTypeSet types2(PREFERENCES, THEMES);
162  EXPECT_TRUE(registrar_->ConfigureDataTypes(types2, types1).Equals(types2));
163  {
164    syncer::ModelSafeRoutingInfo expected_routing_info;
165    expected_routing_info[PREFERENCES] = syncer::GROUP_PASSIVE;
166    expected_routing_info[THEMES] = syncer::GROUP_PASSIVE;
167    ExpectRoutingInfo(registrar_.get(), expected_routing_info);
168  }
169  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
170  EXPECT_TRUE(types2.Equals(registrar_->GetLastConfiguredTypes()));
171
172  // Remove.
173  EXPECT_TRUE(registrar_->ConfigureDataTypes(ModelTypeSet(), types2).Empty());
174  ExpectRoutingInfo(registrar_.get(), syncer::ModelSafeRoutingInfo());
175  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
176  EXPECT_TRUE(ModelTypeSet().Equals(registrar_->GetLastConfiguredTypes()));
177}
178
179TEST_F(SyncBackendRegistrarTest, ActivateDeactivateUIDataType) {
180  InSequence in_sequence;
181  registrar_->SetInitialTypes(ModelTypeSet());
182
183  // Should do nothing.
184  TriggerChanges(registrar_.get(), BOOKMARKS);
185
186  StrictMock<sync_driver::ChangeProcessorMock> change_processor_mock;
187  EXPECT_CALL(change_processor_mock, StartImpl());
188  EXPECT_CALL(change_processor_mock, IsRunning())
189      .WillRepeatedly(Return(true));
190  EXPECT_CALL(change_processor_mock, ApplyChangesFromSyncModel(NULL, _, _));
191  EXPECT_CALL(change_processor_mock, IsRunning())
192      .WillRepeatedly(Return(true));
193  EXPECT_CALL(change_processor_mock, CommitChangesFromSyncModel());
194  EXPECT_CALL(change_processor_mock, IsRunning())
195      .WillRepeatedly(Return(false));
196
197  const ModelTypeSet types(BOOKMARKS);
198  EXPECT_TRUE(
199      registrar_->ConfigureDataTypes(types, ModelTypeSet()).Equals(types));
200  registrar_->ActivateDataType(BOOKMARKS, syncer::GROUP_UI,
201                             &change_processor_mock,
202                             test_user_share_.user_share());
203  {
204    syncer::ModelSafeRoutingInfo expected_routing_info;
205    expected_routing_info[BOOKMARKS] = syncer::GROUP_UI;
206    ExpectRoutingInfo(registrar_.get(), expected_routing_info);
207  }
208  ExpectHasProcessorsForTypes(*registrar_, types);
209
210  TriggerChanges(registrar_.get(), BOOKMARKS);
211
212  registrar_->DeactivateDataType(BOOKMARKS);
213  ExpectRoutingInfo(registrar_.get(), syncer::ModelSafeRoutingInfo());
214  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
215
216  // Should do nothing.
217  TriggerChanges(registrar_.get(), BOOKMARKS);
218}
219
220TEST_F(SyncBackendRegistrarTest, ActivateDeactivateNonUIDataType) {
221  InSequence in_sequence;
222  registrar_->SetInitialTypes(ModelTypeSet());
223
224  // Should do nothing.
225  TriggerChanges(registrar_.get(), AUTOFILL);
226
227  StrictMock<sync_driver::ChangeProcessorMock> change_processor_mock;
228  EXPECT_CALL(change_processor_mock, StartImpl());
229  EXPECT_CALL(change_processor_mock, IsRunning())
230      .WillRepeatedly(Return(true));
231  EXPECT_CALL(change_processor_mock, ApplyChangesFromSyncModel(NULL, _, _));
232  EXPECT_CALL(change_processor_mock, IsRunning())
233      .WillRepeatedly(Return(true));
234  EXPECT_CALL(change_processor_mock, CommitChangesFromSyncModel());
235  EXPECT_CALL(change_processor_mock, IsRunning())
236      .WillRepeatedly(Return(false));
237
238  const ModelTypeSet types(AUTOFILL);
239  EXPECT_TRUE(
240      registrar_->ConfigureDataTypes(types, ModelTypeSet()).Equals(types));
241
242  base::WaitableEvent done(false, false);
243  BrowserThread::PostTask(
244      BrowserThread::DB,
245      FROM_HERE,
246      base::Bind(&SyncBackendRegistrarTest::TestNonUIDataTypeActivationAsync,
247                 base::Unretained(this),
248                 &change_processor_mock,
249                 &done));
250  done.Wait();
251
252  registrar_->DeactivateDataType(AUTOFILL);
253  ExpectRoutingInfo(registrar_.get(), syncer::ModelSafeRoutingInfo());
254  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
255
256  // Should do nothing.
257  TriggerChanges(registrar_.get(), AUTOFILL);
258}
259
260class SyncBackendRegistrarShutdownTest : public testing::Test {
261 public:
262  void BlockDBThread() {
263    EXPECT_FALSE(db_thread_lock_.Try());
264
265    db_thread_blocked_.Signal();
266    base::AutoLock l(db_thread_lock_);
267  }
268
269 protected:
270  friend class TestRegistrar;
271
272  SyncBackendRegistrarShutdownTest()
273      : thread_bundle_(content::TestBrowserThreadBundle::REAL_DB_THREAD |
274                       content::TestBrowserThreadBundle::REAL_FILE_THREAD |
275                       content::TestBrowserThreadBundle::REAL_IO_THREAD),
276        db_thread_blocked_(false, false) {
277    quit_closure_ = run_loop_.QuitClosure();
278  }
279
280  virtual ~SyncBackendRegistrarShutdownTest() {}
281
282  void PostQuitOnUIMessageLoop() {
283    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, quit_closure_);
284  }
285
286  content::TestBrowserThreadBundle thread_bundle_;
287  TestingProfile profile_;
288  base::WaitableEvent db_thread_blocked_;
289  base::Lock db_thread_lock_;
290  base::RunLoop run_loop_;
291  base::Closure quit_closure_;
292};
293
294// Wrap SyncBackendRegistrar so that we can monitor its lifetime.
295class TestRegistrar : public SyncBackendRegistrar {
296 public:
297  explicit TestRegistrar(Profile* profile,
298                         SyncBackendRegistrarShutdownTest* test)
299      : SyncBackendRegistrar("test", profile, scoped_ptr<base::Thread>()),
300        test_(test) {}
301
302  virtual ~TestRegistrar() { test_->PostQuitOnUIMessageLoop(); }
303
304 private:
305  SyncBackendRegistrarShutdownTest* test_;
306};
307
308TEST_F(SyncBackendRegistrarShutdownTest, BlockingShutdown) {
309  // Take ownership of |db_thread_lock_| so that the DB thread can't acquire it.
310  db_thread_lock_.Acquire();
311
312  // This will block the DB thread by waiting on |db_thread_lock_|.
313  BrowserThread::PostTask(
314      BrowserThread::DB,
315      FROM_HERE,
316      base::Bind(&SyncBackendRegistrarShutdownTest::BlockDBThread,
317                 base::Unretained(this)));
318
319  scoped_ptr<TestRegistrar> registrar(new TestRegistrar(&profile_, this));
320  base::Thread* sync_thread = registrar->sync_thread();
321
322  // Stop here until the DB thread gets a chance to run and block on the lock.
323  // Please note that since the task above didn't finish, the task to
324  // initialize the worker on the DB thread hasn't had a chance to run yet too.
325  // Which means ModelSafeWorker::SetWorkingLoopToCurrent hasn't been called
326  // for the DB worker.
327  db_thread_blocked_.Wait();
328
329  registrar->SetInitialTypes(ModelTypeSet());
330
331  // Start the shutdown.
332  registrar->RequestWorkerStopOnUIThread();
333
334  sync_thread->message_loop()->PostTask(
335      FROM_HERE,
336      base::Bind(&SyncBackendRegistrar::Shutdown,
337                 base::Unretained(registrar.release())));
338
339  // The test verifies that the sync thread doesn't block because
340  // of the blocked DB thread and can finish the shutdown.
341  sync_thread->message_loop()->RunUntilIdle();
342
343  db_thread_lock_.Release();
344
345  // Run the main thread loop until all workers have been removed and the
346  // registrar destroyed.
347  run_loop_.Run();
348}
349
350}  // namespace
351
352}  // namespace browser_sync
353