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 "base/callback.h"
6#include "base/message_loop/message_loop.h"
7#include "components/sync_driver/fake_data_type_controller.h"
8#include "components/sync_driver/model_association_manager.h"
9#include "testing/gmock/include/gmock/gmock.h"
10#include "testing/gtest/include/gtest/gtest.h"
11
12using ::testing::_;
13
14namespace sync_driver {
15
16class MockModelAssociationManagerDelegate :
17    public ModelAssociationManagerDelegate {
18 public:
19  MockModelAssociationManagerDelegate() {}
20  ~MockModelAssociationManagerDelegate() {}
21  MOCK_METHOD2(OnSingleDataTypeAssociationDone,
22      void(syncer::ModelType type,
23      const syncer::DataTypeAssociationStats& association_stats));
24  MOCK_METHOD2(OnSingleDataTypeWillStop,
25               void(syncer::ModelType, const syncer::SyncError& error));
26  MOCK_METHOD1(OnModelAssociationDone, void(
27      const DataTypeManager::ConfigureResult& result));
28};
29
30FakeDataTypeController* GetController(
31    const DataTypeController::TypeMap& controllers,
32    syncer::ModelType model_type) {
33  DataTypeController::TypeMap::const_iterator it =
34      controllers.find(model_type);
35  if (it == controllers.end()) {
36    return NULL;
37  }
38  return (FakeDataTypeController*)(it->second.get());
39}
40
41ACTION_P(VerifyResult, expected_result) {
42  EXPECT_EQ(arg0.status, expected_result.status);
43  EXPECT_TRUE(arg0.requested_types.Equals(expected_result.requested_types));
44}
45
46class SyncModelAssociationManagerTest : public testing::Test {
47 public:
48  SyncModelAssociationManagerTest() {
49  }
50
51 protected:
52  base::MessageLoopForUI ui_loop_;
53  MockModelAssociationManagerDelegate delegate_;
54  DataTypeController::TypeMap controllers_;
55};
56
57// Start a type and make sure ModelAssociationManager callst the |Start|
58// method and calls the callback when it is done.
59TEST_F(SyncModelAssociationManagerTest, SimpleModelStart) {
60  controllers_[syncer::BOOKMARKS] =
61      new FakeDataTypeController(syncer::BOOKMARKS);
62  controllers_[syncer::APPS] =
63      new FakeDataTypeController(syncer::APPS);
64  ModelAssociationManager model_association_manager(&controllers_,
65                                                    &delegate_);
66  syncer::ModelTypeSet types(syncer::BOOKMARKS, syncer::APPS);
67  DataTypeManager::ConfigureResult expected_result(DataTypeManager::OK, types);
68  EXPECT_CALL(delegate_, OnModelAssociationDone(_)).
69              WillOnce(VerifyResult(expected_result));
70
71  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
72            DataTypeController::NOT_RUNNING);
73  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
74            DataTypeController::NOT_RUNNING);
75
76  // Initialize() kicks off model loading.
77  model_association_manager.Initialize(types);
78
79  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
80            DataTypeController::MODEL_LOADED);
81  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
82            DataTypeController::MODEL_LOADED);
83
84  model_association_manager.StartAssociationAsync(types);
85
86  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
87            DataTypeController::ASSOCIATING);
88  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
89            DataTypeController::ASSOCIATING);
90  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
91      DataTypeController::OK);
92  GetController(controllers_, syncer::APPS)->FinishStart(
93      DataTypeController::OK);
94}
95
96// Start a type and call stop before it finishes associating.
97TEST_F(SyncModelAssociationManagerTest, StopModelBeforeFinish) {
98  controllers_[syncer::BOOKMARKS] =
99      new FakeDataTypeController(syncer::BOOKMARKS);
100  ModelAssociationManager model_association_manager(
101      &controllers_,
102      &delegate_);
103
104  syncer::ModelTypeSet types;
105  types.Put(syncer::BOOKMARKS);
106
107  DataTypeManager::ConfigureResult expected_result(DataTypeManager::ABORTED,
108                                                   types);
109
110  EXPECT_CALL(delegate_, OnModelAssociationDone(_)).
111              WillOnce(VerifyResult(expected_result));
112  EXPECT_CALL(delegate_,
113              OnSingleDataTypeWillStop(syncer::BOOKMARKS, _));
114
115  model_association_manager.Initialize(types);
116  model_association_manager.StartAssociationAsync(types);
117
118  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
119            DataTypeController::ASSOCIATING);
120  model_association_manager.Stop();
121  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
122            DataTypeController::NOT_RUNNING);
123}
124
125// Start a type, let it finish and then call stop.
126TEST_F(SyncModelAssociationManagerTest, StopAfterFinish) {
127  controllers_[syncer::BOOKMARKS] =
128      new FakeDataTypeController(syncer::BOOKMARKS);
129  ModelAssociationManager model_association_manager(
130      &controllers_,
131      &delegate_);
132  syncer::ModelTypeSet types;
133  types.Put(syncer::BOOKMARKS);
134  DataTypeManager::ConfigureResult expected_result(DataTypeManager::OK, types);
135  EXPECT_CALL(delegate_, OnModelAssociationDone(_)).
136              WillOnce(VerifyResult(expected_result));
137  EXPECT_CALL(delegate_,
138              OnSingleDataTypeWillStop(syncer::BOOKMARKS, _));
139
140  model_association_manager.Initialize(types);
141  model_association_manager.StartAssociationAsync(types);
142
143  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
144            DataTypeController::ASSOCIATING);
145  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
146      DataTypeController::OK);
147
148  model_association_manager.Stop();
149  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
150            DataTypeController::NOT_RUNNING);
151}
152
153// Make a type fail model association and verify correctness.
154TEST_F(SyncModelAssociationManagerTest, TypeFailModelAssociation) {
155  controllers_[syncer::BOOKMARKS] =
156      new FakeDataTypeController(syncer::BOOKMARKS);
157  ModelAssociationManager model_association_manager(
158      &controllers_,
159      &delegate_);
160  syncer::ModelTypeSet types;
161  types.Put(syncer::BOOKMARKS);
162  DataTypeManager::ConfigureResult expected_result(DataTypeManager::OK, types);
163  EXPECT_CALL(delegate_,
164              OnSingleDataTypeWillStop(syncer::BOOKMARKS, _));
165  EXPECT_CALL(delegate_, OnModelAssociationDone(_)).
166              WillOnce(VerifyResult(expected_result));
167
168  model_association_manager.Initialize(types);
169  model_association_manager.StartAssociationAsync(types);
170
171  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
172            DataTypeController::ASSOCIATING);
173  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
174      DataTypeController::ASSOCIATION_FAILED);
175  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
176            DataTypeController::NOT_RUNNING);
177}
178
179// Ensure configuring stops when a type returns a unrecoverable error.
180TEST_F(SyncModelAssociationManagerTest, TypeReturnUnrecoverableError) {
181  controllers_[syncer::BOOKMARKS] =
182      new FakeDataTypeController(syncer::BOOKMARKS);
183  ModelAssociationManager model_association_manager(
184      &controllers_,
185      &delegate_);
186  syncer::ModelTypeSet types;
187  types.Put(syncer::BOOKMARKS);
188  DataTypeManager::ConfigureResult expected_result(
189      DataTypeManager::UNRECOVERABLE_ERROR, types);
190  EXPECT_CALL(delegate_,
191              OnSingleDataTypeWillStop(syncer::BOOKMARKS, _));
192  EXPECT_CALL(delegate_, OnModelAssociationDone(_)).
193              WillOnce(VerifyResult(expected_result));
194
195  model_association_manager.Initialize(types);
196
197  model_association_manager.StartAssociationAsync(types);
198
199  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
200            DataTypeController::ASSOCIATING);
201  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
202      DataTypeController::UNRECOVERABLE_ERROR);
203}
204
205TEST_F(SyncModelAssociationManagerTest, SlowTypeAsFailedType) {
206  controllers_[syncer::BOOKMARKS] =
207      new FakeDataTypeController(syncer::BOOKMARKS);
208  controllers_[syncer::APPS] =
209      new FakeDataTypeController(syncer::APPS);
210  GetController(controllers_, syncer::BOOKMARKS)->SetDelayModelLoad();
211  ModelAssociationManager model_association_manager(&controllers_,
212                                                    &delegate_);
213  syncer::ModelTypeSet types;
214  types.Put(syncer::BOOKMARKS);
215  types.Put(syncer::APPS);
216
217  syncer::ModelTypeSet expected_types_unfinished;
218  expected_types_unfinished.Put(syncer::BOOKMARKS);
219  DataTypeManager::ConfigureResult expected_result_partially_done(
220      DataTypeManager::OK, types);
221
222  EXPECT_CALL(delegate_, OnModelAssociationDone(_)).
223              WillOnce(VerifyResult(expected_result_partially_done));
224
225  model_association_manager.Initialize(types);
226  model_association_manager.StartAssociationAsync(types);
227  GetController(controllers_, syncer::APPS)->FinishStart(
228      DataTypeController::OK);
229
230  EXPECT_CALL(delegate_,
231              OnSingleDataTypeWillStop(syncer::BOOKMARKS, _));
232  model_association_manager.GetTimerForTesting()->user_task().Run();
233
234  EXPECT_EQ(DataTypeController::NOT_RUNNING,
235            GetController(controllers_, syncer::BOOKMARKS)->state());
236}
237
238TEST_F(SyncModelAssociationManagerTest, StartMultipleTimes) {
239  controllers_[syncer::BOOKMARKS] =
240      new FakeDataTypeController(syncer::BOOKMARKS);
241  controllers_[syncer::APPS] =
242      new FakeDataTypeController(syncer::APPS);
243  ModelAssociationManager model_association_manager(&controllers_,
244                                                    &delegate_);
245  syncer::ModelTypeSet types;
246  types.Put(syncer::BOOKMARKS);
247  types.Put(syncer::APPS);
248
249  DataTypeManager::ConfigureResult result_1st(
250      DataTypeManager::OK,
251      syncer::ModelTypeSet(syncer::BOOKMARKS));
252  DataTypeManager::ConfigureResult result_2nd(
253      DataTypeManager::OK,
254      syncer::ModelTypeSet(syncer::APPS));
255  EXPECT_CALL(delegate_, OnModelAssociationDone(_)).
256      Times(2).
257      WillOnce(VerifyResult(result_1st)).
258      WillOnce(VerifyResult(result_2nd));
259
260  model_association_manager.Initialize(types);
261
262  // Start BOOKMARKS first.
263  model_association_manager.StartAssociationAsync(
264      syncer::ModelTypeSet(syncer::BOOKMARKS));
265  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
266            DataTypeController::ASSOCIATING);
267  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
268            DataTypeController::MODEL_LOADED);
269
270  // Finish BOOKMARKS association.
271  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
272      DataTypeController::OK);
273  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
274            DataTypeController::RUNNING);
275  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
276            DataTypeController::MODEL_LOADED);
277
278  // Start APPS next.
279  model_association_manager.StartAssociationAsync(
280      syncer::ModelTypeSet(syncer::APPS));
281  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
282            DataTypeController::ASSOCIATING);
283  GetController(controllers_, syncer::APPS)->FinishStart(
284      DataTypeController::OK);
285  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
286            DataTypeController::RUNNING);
287}
288
289// Test that model that failed to load between initialization and association
290// is reported and stopped properly.
291TEST_F(SyncModelAssociationManagerTest, ModelLoadFailBeforeAssociationStart) {
292  controllers_[syncer::BOOKMARKS] =
293      new FakeDataTypeController(syncer::BOOKMARKS);
294  GetController(controllers_, syncer::BOOKMARKS)->SetModelLoadError(
295      syncer::SyncError(FROM_HERE, syncer::SyncError::DATATYPE_ERROR,
296                        "", syncer::BOOKMARKS));
297  ModelAssociationManager model_association_manager(
298      &controllers_,
299      &delegate_);
300  syncer::ModelTypeSet types;
301  types.Put(syncer::BOOKMARKS);
302  DataTypeManager::ConfigureResult expected_result(DataTypeManager::OK, types);
303  EXPECT_CALL(delegate_,
304              OnSingleDataTypeWillStop(syncer::BOOKMARKS, _));
305  EXPECT_CALL(delegate_, OnModelAssociationDone(_)).
306              WillOnce(VerifyResult(expected_result));
307
308  model_association_manager.Initialize(types);
309  EXPECT_EQ(DataTypeController::NOT_RUNNING,
310            GetController(controllers_, syncer::BOOKMARKS)->state());
311  model_association_manager.StartAssociationAsync(types);
312  EXPECT_EQ(DataTypeController::NOT_RUNNING,
313            GetController(controllers_, syncer::BOOKMARKS)->state());
314}
315
316// Test that a runtime error is handled by stopping the type.
317TEST_F(SyncModelAssociationManagerTest, StopAfterConfiguration) {
318  controllers_[syncer::BOOKMARKS] =
319      new FakeDataTypeController(syncer::BOOKMARKS);
320  ModelAssociationManager model_association_manager(
321      &controllers_,
322      &delegate_);
323  syncer::ModelTypeSet types;
324  types.Put(syncer::BOOKMARKS);
325  DataTypeManager::ConfigureResult expected_result(DataTypeManager::OK, types);
326  EXPECT_CALL(delegate_, OnModelAssociationDone(_)).
327              WillOnce(VerifyResult(expected_result));
328
329  model_association_manager.Initialize(types);
330  model_association_manager.StartAssociationAsync(types);
331
332  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
333            DataTypeController::ASSOCIATING);
334  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
335      DataTypeController::OK);
336  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
337            DataTypeController::RUNNING);
338
339  testing::Mock::VerifyAndClearExpectations(&delegate_);
340  EXPECT_CALL(delegate_,
341              OnSingleDataTypeWillStop(syncer::BOOKMARKS, _));
342  syncer::SyncError error(FROM_HERE,
343                          syncer::SyncError::DATATYPE_ERROR,
344                          "error",
345                          syncer::BOOKMARKS);
346  GetController(controllers_, syncer::BOOKMARKS)
347      ->OnSingleDataTypeUnrecoverableError(error);
348}
349
350TEST_F(SyncModelAssociationManagerTest, AbortDuringAssociation) {
351  controllers_[syncer::BOOKMARKS] =
352      new FakeDataTypeController(syncer::BOOKMARKS);
353  controllers_[syncer::APPS] =
354      new FakeDataTypeController(syncer::APPS);
355  ModelAssociationManager model_association_manager(&controllers_,
356                                                    &delegate_);
357  syncer::ModelTypeSet types;
358  types.Put(syncer::BOOKMARKS);
359  types.Put(syncer::APPS);
360
361  syncer::ModelTypeSet expected_types_unfinished;
362  expected_types_unfinished.Put(syncer::BOOKMARKS);
363  DataTypeManager::ConfigureResult expected_result_partially_done(
364      DataTypeManager::OK, types);
365
366  EXPECT_CALL(delegate_, OnModelAssociationDone(_)).
367              WillOnce(VerifyResult(expected_result_partially_done));
368
369  model_association_manager.Initialize(types);
370  model_association_manager.StartAssociationAsync(types);
371  GetController(controllers_, syncer::APPS)->FinishStart(
372      DataTypeController::OK);
373  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
374            DataTypeController::RUNNING);
375  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
376            DataTypeController::ASSOCIATING);
377
378  EXPECT_CALL(delegate_,
379              OnSingleDataTypeWillStop(syncer::BOOKMARKS, _));
380  model_association_manager.GetTimerForTesting()->user_task().Run();
381
382  EXPECT_EQ(DataTypeController::NOT_RUNNING,
383            GetController(controllers_, syncer::BOOKMARKS)->state());
384}
385
386}  // namespace sync_driver
387