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 "base/callback.h"
6#include "base/message_loop/message_loop.h"
7#include "chrome/browser/sync/glue/fake_data_type_controller.h"
8#include "chrome/browser/sync/glue/model_association_manager.h"
9#include "content/public/test/test_browser_thread.h"
10#include "testing/gmock/include/gmock/gmock.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13using ::testing::_;
14namespace browser_sync {
15class MockModelAssociationResultProcessor :
16    public ModelAssociationResultProcessor {
17 public:
18  MockModelAssociationResultProcessor() {}
19  ~MockModelAssociationResultProcessor() {}
20  MOCK_METHOD2(OnSingleDataTypeAssociationDone,
21               void(syncer::ModelType type,
22                    const syncer::DataTypeAssociationStats& association_stats));
23  MOCK_METHOD1(OnModelAssociationDone, void(
24      const DataTypeManager::ConfigureResult& result));
25  MOCK_METHOD0(OnTypesLoaded, void());
26};
27
28FakeDataTypeController* GetController(
29    const DataTypeController::TypeMap& controllers,
30    syncer::ModelType model_type) {
31  DataTypeController::TypeMap::const_iterator it =
32      controllers.find(model_type);
33  if (it == controllers.end()) {
34    return NULL;
35  }
36  return (FakeDataTypeController*)(it->second.get());
37}
38
39ACTION_P(VerifyResult, expected_result) {
40  EXPECT_EQ(arg0.status, expected_result.status);
41  EXPECT_TRUE(arg0.requested_types.Equals(expected_result.requested_types));
42  EXPECT_EQ(arg0.failed_data_types.size(),
43            expected_result.failed_data_types.size());
44
45  if (arg0.failed_data_types.size() ==
46          expected_result.failed_data_types.size()) {
47    std::map<syncer::ModelType, syncer::SyncError>::const_iterator it1, it2;
48    for (it1 = arg0.failed_data_types.begin(),
49         it2 = expected_result.failed_data_types.begin();
50         it1 != arg0.failed_data_types.end();
51         ++it1, ++it2) {
52      EXPECT_EQ((*it1).first, (*it2).first);
53    }
54  }
55
56  EXPECT_TRUE(arg0.waiting_to_start.Equals(expected_result.waiting_to_start));
57}
58
59class SyncModelAssociationManagerTest : public testing::Test {
60 public:
61  SyncModelAssociationManagerTest() :
62      ui_thread_(content::BrowserThread::UI, &ui_loop_) {
63  }
64
65 protected:
66  base::MessageLoopForUI ui_loop_;
67  content::TestBrowserThread ui_thread_;
68  MockModelAssociationResultProcessor result_processor_;
69  DataTypeController::TypeMap controllers_;
70};
71
72// Start a type and make sure ModelAssociationManager callst the |Start|
73// method and calls the callback when it is done.
74TEST_F(SyncModelAssociationManagerTest, SimpleModelStart) {
75  controllers_[syncer::BOOKMARKS] =
76      new FakeDataTypeController(syncer::BOOKMARKS);
77  ModelAssociationManager model_association_manager(&controllers_,
78                                                    &result_processor_);
79  syncer::ModelTypeSet types;
80  types.Put(syncer::BOOKMARKS);
81  DataTypeManager::ConfigureResult expected_result(
82      DataTypeManager::OK,
83      types,
84      std::map<syncer::ModelType, syncer::SyncError>(),
85      syncer::ModelTypeSet(),
86      syncer::ModelTypeSet());
87  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
88              WillOnce(VerifyResult(expected_result));
89
90  model_association_manager.Initialize(types);
91  model_association_manager.StopDisabledTypes();
92  model_association_manager.StartAssociationAsync(types);
93
94  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
95            DataTypeController::ASSOCIATING);
96  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
97      DataTypeController::OK);
98}
99
100// Start a type and call stop before it finishes associating.
101TEST_F(SyncModelAssociationManagerTest, StopModelBeforeFinish) {
102  controllers_[syncer::BOOKMARKS] =
103      new FakeDataTypeController(syncer::BOOKMARKS);
104  ModelAssociationManager model_association_manager(
105      &controllers_,
106      &result_processor_);
107
108  syncer::ModelTypeSet types;
109  types.Put(syncer::BOOKMARKS);
110
111  DataTypeManager::ConfigureResult expected_result(
112      DataTypeManager::ABORTED,
113      types,
114      std::map<syncer::ModelType, syncer::SyncError>(),
115      syncer::ModelTypeSet(),
116      syncer::ModelTypeSet());
117
118  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
119              WillOnce(VerifyResult(expected_result));
120
121  model_association_manager.Initialize(types);
122  model_association_manager.StopDisabledTypes();
123  model_association_manager.StartAssociationAsync(types);
124
125  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
126            DataTypeController::ASSOCIATING);
127  model_association_manager.Stop();
128  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
129            DataTypeController::NOT_RUNNING);
130}
131
132// Start a type, let it finish and then call stop.
133TEST_F(SyncModelAssociationManagerTest, StopAfterFinish) {
134  controllers_[syncer::BOOKMARKS] =
135      new FakeDataTypeController(syncer::BOOKMARKS);
136  ModelAssociationManager model_association_manager(
137      &controllers_,
138      &result_processor_);
139  syncer::ModelTypeSet types;
140  types.Put(syncer::BOOKMARKS);
141  DataTypeManager::ConfigureResult expected_result(
142      DataTypeManager::OK,
143      types,
144      std::map<syncer::ModelType, syncer::SyncError>(),
145      syncer::ModelTypeSet(),
146      syncer::ModelTypeSet());
147  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
148              WillOnce(VerifyResult(expected_result));
149
150  model_association_manager.Initialize(types);
151  model_association_manager.StopDisabledTypes();
152  model_association_manager.StartAssociationAsync(types);
153
154  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
155            DataTypeController::ASSOCIATING);
156  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
157      DataTypeController::OK);
158
159  model_association_manager.Stop();
160  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
161            DataTypeController::NOT_RUNNING);
162}
163
164// Make a type fail model association and verify correctness.
165TEST_F(SyncModelAssociationManagerTest, TypeFailModelAssociation) {
166  controllers_[syncer::BOOKMARKS] =
167      new FakeDataTypeController(syncer::BOOKMARKS);
168  ModelAssociationManager model_association_manager(
169      &controllers_,
170      &result_processor_);
171  syncer::ModelTypeSet types;
172  types.Put(syncer::BOOKMARKS);
173  std::map<syncer::ModelType, syncer::SyncError> errors;
174  syncer::SyncError error(FROM_HERE,
175                          syncer::SyncError::DATATYPE_ERROR,
176                          "Failed",
177                          syncer::BOOKMARKS);
178  errors[syncer::BOOKMARKS] = error;
179  DataTypeManager::ConfigureResult expected_result(
180      DataTypeManager::PARTIAL_SUCCESS,
181      types,
182      errors,
183      syncer::ModelTypeSet(),
184      syncer::ModelTypeSet());
185  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
186              WillOnce(VerifyResult(expected_result));
187
188  model_association_manager.Initialize(types);
189  model_association_manager.StopDisabledTypes();
190  model_association_manager.StartAssociationAsync(types);
191
192  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
193            DataTypeController::ASSOCIATING);
194  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
195      DataTypeController::ASSOCIATION_FAILED);
196  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
197            DataTypeController::NOT_RUNNING);
198}
199
200// Ensure configuring stops when a type returns a unrecoverable error.
201TEST_F(SyncModelAssociationManagerTest, TypeReturnUnrecoverableError) {
202  controllers_[syncer::BOOKMARKS] =
203      new FakeDataTypeController(syncer::BOOKMARKS);
204  ModelAssociationManager model_association_manager(
205      &controllers_,
206      &result_processor_);
207  syncer::ModelTypeSet types;
208  types.Put(syncer::BOOKMARKS);
209  std::map<syncer::ModelType, syncer::SyncError> errors;
210  syncer::SyncError error(FROM_HERE,
211                          syncer::SyncError::DATATYPE_ERROR,
212                          "Failed",
213                          syncer::BOOKMARKS);
214  errors[syncer::BOOKMARKS] = error;
215  DataTypeManager::ConfigureResult expected_result(
216      DataTypeManager::UNRECOVERABLE_ERROR,
217      types,
218      errors,
219      syncer::ModelTypeSet(),
220      syncer::ModelTypeSet());
221  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
222              WillOnce(VerifyResult(expected_result));
223
224  model_association_manager.Initialize(types);
225  model_association_manager.StopDisabledTypes();
226  model_association_manager.StartAssociationAsync(types);
227
228  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
229            DataTypeController::ASSOCIATING);
230  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
231      DataTypeController::UNRECOVERABLE_ERROR);
232}
233
234TEST_F(SyncModelAssociationManagerTest, InitializeAbortsLoad) {
235  controllers_[syncer::BOOKMARKS] =
236      new FakeDataTypeController(syncer::BOOKMARKS);
237  controllers_[syncer::THEMES] =
238      new FakeDataTypeController(syncer::THEMES);
239
240  GetController(controllers_, syncer::BOOKMARKS)->SetDelayModelLoad();
241  ModelAssociationManager model_association_manager(&controllers_,
242                                                    &result_processor_);
243  syncer::ModelTypeSet types(syncer::BOOKMARKS, syncer::THEMES);
244
245  syncer::ModelTypeSet expected_types_waiting_to_load;
246  expected_types_waiting_to_load.Put(syncer::BOOKMARKS);
247  DataTypeManager::ConfigureResult expected_result_partially_done(
248      DataTypeManager::PARTIAL_SUCCESS,
249      types,
250      std::map<syncer::ModelType, syncer::SyncError>(),
251      expected_types_waiting_to_load,
252      syncer::ModelTypeSet());
253
254  model_association_manager.Initialize(types);
255  model_association_manager.StopDisabledTypes();
256
257  model_association_manager.StartAssociationAsync(types);
258
259  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
260              WillOnce(VerifyResult(expected_result_partially_done));
261
262  base::OneShotTimer<ModelAssociationManager>* timer =
263      model_association_manager.GetTimerForTesting();
264
265  base::Closure task = timer->user_task();
266  timer->Stop();
267  task.Run();  // Bookmark load times out here.
268
269  // Apps finishes associating here.
270  GetController(controllers_, syncer::THEMES)->FinishStart(
271      DataTypeController::OK);
272
273  // At this point, BOOKMARKS is still waiting to load (as evidenced by
274  // expected_result_partially_done). If we schedule another Initialize (which
275  // could happen in practice due to reconfiguration), this should abort
276  // BOOKMARKS. Aborting will call ModelLoadCallback, but the
277  // ModelAssociationManager should be smart enough to know that this is not due
278  // to the type having completed loading.
279  EXPECT_CALL(result_processor_, OnTypesLoaded()).Times(0);
280
281  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
282            DataTypeController::MODEL_STARTING);
283
284  model_association_manager.Initialize(types);
285  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
286            DataTypeController::NOT_RUNNING);
287
288  DataTypeManager::ConfigureResult expected_result_done(
289      DataTypeManager::OK,
290      types,
291      std::map<syncer::ModelType, syncer::SyncError>(),
292      syncer::ModelTypeSet(),
293      syncer::ModelTypeSet());
294  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
295              WillOnce(VerifyResult(expected_result_done));
296
297  model_association_manager.StopDisabledTypes();
298  model_association_manager.StartAssociationAsync(types);
299
300  GetController(controllers_,
301                syncer::BOOKMARKS)->SimulateModelLoadFinishing();
302  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
303      DataTypeController::OK);
304}
305
306// Start 2 types. One of which timeout loading. Ensure that type is
307// fully configured eventually.
308TEST_F(SyncModelAssociationManagerTest, ModelStartWithSlowLoadingType) {
309  controllers_[syncer::BOOKMARKS] =
310      new FakeDataTypeController(syncer::BOOKMARKS);
311  controllers_[syncer::APPS] =
312      new FakeDataTypeController(syncer::APPS);
313  GetController(controllers_, syncer::BOOKMARKS)->SetDelayModelLoad();
314  ModelAssociationManager model_association_manager(&controllers_,
315                                                    &result_processor_);
316  syncer::ModelTypeSet types;
317  types.Put(syncer::BOOKMARKS);
318  types.Put(syncer::APPS);
319
320  syncer::ModelTypeSet expected_types_waiting_to_load;
321  expected_types_waiting_to_load.Put(syncer::BOOKMARKS);
322  DataTypeManager::ConfigureResult expected_result_partially_done(
323      DataTypeManager::PARTIAL_SUCCESS,
324      types,
325      std::map<syncer::ModelType, syncer::SyncError>(),
326      expected_types_waiting_to_load,
327      syncer::ModelTypeSet());
328
329  DataTypeManager::ConfigureResult expected_result_done(
330      DataTypeManager::OK,
331      types,
332      std::map<syncer::ModelType, syncer::SyncError>(),
333      syncer::ModelTypeSet(),
334      syncer::ModelTypeSet());
335
336  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
337              WillOnce(VerifyResult(expected_result_partially_done));
338  EXPECT_CALL(result_processor_, OnTypesLoaded());
339
340  model_association_manager.Initialize(types);
341  model_association_manager.StopDisabledTypes();
342  model_association_manager.StartAssociationAsync(types);
343
344  base::OneShotTimer<ModelAssociationManager>* timer =
345      model_association_manager.GetTimerForTesting();
346
347  // Note: Independent of the timeout value this test is not flaky.
348  // The reason is timer posts a task which would never be executed
349  // as we dont let the message loop run.
350  base::Closure task = timer->user_task();
351  timer->Stop();
352  task.Run();
353
354  // Simulate delayed loading of bookmark model.
355  GetController(controllers_, syncer::APPS)->FinishStart(
356      DataTypeController::OK);
357
358  GetController(controllers_,
359                syncer::BOOKMARKS)->SimulateModelLoadFinishing();
360
361  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
362              WillOnce(VerifyResult(expected_result_done));
363
364  // Do it once more to associate bookmarks.
365  model_association_manager.Initialize(types);
366  model_association_manager.StopDisabledTypes();
367  model_association_manager.StartAssociationAsync(types);
368
369  GetController(controllers_,
370                syncer::BOOKMARKS)->SimulateModelLoadFinishing();
371
372  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
373      DataTypeController::OK);
374}
375
376TEST_F(SyncModelAssociationManagerTest, StartMultipleTimes) {
377  controllers_[syncer::BOOKMARKS] =
378      new FakeDataTypeController(syncer::BOOKMARKS);
379  controllers_[syncer::APPS] =
380      new FakeDataTypeController(syncer::APPS);
381  ModelAssociationManager model_association_manager(&controllers_,
382                                                    &result_processor_);
383  syncer::ModelTypeSet types;
384  types.Put(syncer::BOOKMARKS);
385  types.Put(syncer::APPS);
386
387  DataTypeManager::ConfigureResult result_1st(
388      DataTypeManager::OK,
389      syncer::ModelTypeSet(syncer::BOOKMARKS),
390      std::map<syncer::ModelType, syncer::SyncError>(),
391      syncer::ModelTypeSet(),
392      syncer::ModelTypeSet());
393  DataTypeManager::ConfigureResult result_2nd(
394      DataTypeManager::OK,
395      syncer::ModelTypeSet(syncer::APPS),
396      std::map<syncer::ModelType, syncer::SyncError>(),
397      syncer::ModelTypeSet(),
398      syncer::ModelTypeSet());
399  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
400      Times(2).
401      WillOnce(VerifyResult(result_1st)).
402      WillOnce(VerifyResult(result_2nd));
403
404  model_association_manager.Initialize(types);
405  model_association_manager.StopDisabledTypes();
406
407  // Start BOOKMARKS first.
408  model_association_manager.StartAssociationAsync(
409      syncer::ModelTypeSet(syncer::BOOKMARKS));
410  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
411            DataTypeController::ASSOCIATING);
412  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
413            DataTypeController::NOT_RUNNING);
414
415  // Finish BOOKMARKS association.
416  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
417      DataTypeController::OK);
418  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
419            DataTypeController::RUNNING);
420  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
421            DataTypeController::NOT_RUNNING);
422
423  // Start APPS next.
424  model_association_manager.StartAssociationAsync(
425      syncer::ModelTypeSet(syncer::APPS));
426  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
427            DataTypeController::ASSOCIATING);
428  GetController(controllers_, syncer::APPS)->FinishStart(
429      DataTypeController::OK);
430  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
431            DataTypeController::RUNNING);
432}
433
434}  // namespace browser_sync
435