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.
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"
14 namespace browser_sync {
15 class MockModelAssociationResultProcessor :
16 public ModelAssociationResultProcessor {
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());
28 FakeDataTypeController* 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()) {
36 return (FakeDataTypeController*)(it->second.get());
39 ACTION_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());
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();
52 EXPECT_EQ((*it1).first, (*it2).first);
56 EXPECT_TRUE(arg0.waiting_to_start.Equals(expected_result.waiting_to_start));
59 class SyncModelAssociationManagerTest : public testing::Test {
61 SyncModelAssociationManagerTest() :
62 ui_thread_(content::BrowserThread::UI, &ui_loop_) {
66 base::MessageLoopForUI ui_loop_;
67 content::TestBrowserThread ui_thread_;
68 MockModelAssociationResultProcessor result_processor_;
69 DataTypeController::TypeMap controllers_;
72 // Start a type and make sure ModelAssociationManager callst the |Start|
73 // method and calls the callback when it is done.
74 TEST_F(SyncModelAssociationManagerTest, SimpleModelStart) {
75 controllers_[syncer::BOOKMARKS] =
76 new FakeDataTypeController(syncer::BOOKMARKS);
77 ModelAssociationManager model_association_manager(&controllers_,
79 syncer::ModelTypeSet types;
80 types.Put(syncer::BOOKMARKS);
81 DataTypeManager::ConfigureResult expected_result(
84 std::map<syncer::ModelType, syncer::SyncError>(),
85 syncer::ModelTypeSet(),
86 syncer::ModelTypeSet());
87 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
88 WillOnce(VerifyResult(expected_result));
90 model_association_manager.Initialize(types);
91 model_association_manager.StopDisabledTypes();
92 model_association_manager.StartAssociationAsync(types);
94 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
95 DataTypeController::ASSOCIATING);
96 GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
97 DataTypeController::OK);
100 // Start a type and call stop before it finishes associating.
101 TEST_F(SyncModelAssociationManagerTest, StopModelBeforeFinish) {
102 controllers_[syncer::BOOKMARKS] =
103 new FakeDataTypeController(syncer::BOOKMARKS);
104 ModelAssociationManager model_association_manager(
108 syncer::ModelTypeSet types;
109 types.Put(syncer::BOOKMARKS);
111 DataTypeManager::ConfigureResult expected_result(
112 DataTypeManager::ABORTED,
114 std::map<syncer::ModelType, syncer::SyncError>(),
115 syncer::ModelTypeSet(),
116 syncer::ModelTypeSet());
118 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
119 WillOnce(VerifyResult(expected_result));
121 model_association_manager.Initialize(types);
122 model_association_manager.StopDisabledTypes();
123 model_association_manager.StartAssociationAsync(types);
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);
132 // Start a type, let it finish and then call stop.
133 TEST_F(SyncModelAssociationManagerTest, StopAfterFinish) {
134 controllers_[syncer::BOOKMARKS] =
135 new FakeDataTypeController(syncer::BOOKMARKS);
136 ModelAssociationManager model_association_manager(
139 syncer::ModelTypeSet types;
140 types.Put(syncer::BOOKMARKS);
141 DataTypeManager::ConfigureResult expected_result(
144 std::map<syncer::ModelType, syncer::SyncError>(),
145 syncer::ModelTypeSet(),
146 syncer::ModelTypeSet());
147 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
148 WillOnce(VerifyResult(expected_result));
150 model_association_manager.Initialize(types);
151 model_association_manager.StopDisabledTypes();
152 model_association_manager.StartAssociationAsync(types);
154 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
155 DataTypeController::ASSOCIATING);
156 GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
157 DataTypeController::OK);
159 model_association_manager.Stop();
160 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
161 DataTypeController::NOT_RUNNING);
164 // Make a type fail model association and verify correctness.
165 TEST_F(SyncModelAssociationManagerTest, TypeFailModelAssociation) {
166 controllers_[syncer::BOOKMARKS] =
167 new FakeDataTypeController(syncer::BOOKMARKS);
168 ModelAssociationManager model_association_manager(
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,
178 errors[syncer::BOOKMARKS] = error;
179 DataTypeManager::ConfigureResult expected_result(
180 DataTypeManager::PARTIAL_SUCCESS,
183 syncer::ModelTypeSet(),
184 syncer::ModelTypeSet());
185 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
186 WillOnce(VerifyResult(expected_result));
188 model_association_manager.Initialize(types);
189 model_association_manager.StopDisabledTypes();
190 model_association_manager.StartAssociationAsync(types);
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);
200 // Ensure configuring stops when a type returns a unrecoverable error.
201 TEST_F(SyncModelAssociationManagerTest, TypeReturnUnrecoverableError) {
202 controllers_[syncer::BOOKMARKS] =
203 new FakeDataTypeController(syncer::BOOKMARKS);
204 ModelAssociationManager model_association_manager(
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,
214 errors[syncer::BOOKMARKS] = error;
215 DataTypeManager::ConfigureResult expected_result(
216 DataTypeManager::UNRECOVERABLE_ERROR,
219 syncer::ModelTypeSet(),
220 syncer::ModelTypeSet());
221 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
222 WillOnce(VerifyResult(expected_result));
224 model_association_manager.Initialize(types);
225 model_association_manager.StopDisabledTypes();
226 model_association_manager.StartAssociationAsync(types);
228 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
229 DataTypeController::ASSOCIATING);
230 GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
231 DataTypeController::UNRECOVERABLE_ERROR);
234 TEST_F(SyncModelAssociationManagerTest, InitializeAbortsLoad) {
235 controllers_[syncer::BOOKMARKS] =
236 new FakeDataTypeController(syncer::BOOKMARKS);
237 controllers_[syncer::THEMES] =
238 new FakeDataTypeController(syncer::THEMES);
240 GetController(controllers_, syncer::BOOKMARKS)->SetDelayModelLoad();
241 ModelAssociationManager model_association_manager(&controllers_,
243 syncer::ModelTypeSet types(syncer::BOOKMARKS, syncer::THEMES);
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,
250 std::map<syncer::ModelType, syncer::SyncError>(),
251 expected_types_waiting_to_load,
252 syncer::ModelTypeSet());
254 model_association_manager.Initialize(types);
255 model_association_manager.StopDisabledTypes();
257 model_association_manager.StartAssociationAsync(types);
259 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
260 WillOnce(VerifyResult(expected_result_partially_done));
262 base::OneShotTimer<ModelAssociationManager>* timer =
263 model_association_manager.GetTimerForTesting();
265 base::Closure task = timer->user_task();
267 task.Run(); // Bookmark load times out here.
269 // Apps finishes associating here.
270 GetController(controllers_, syncer::THEMES)->FinishStart(
271 DataTypeController::OK);
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);
281 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
282 DataTypeController::MODEL_STARTING);
284 model_association_manager.Initialize(types);
285 EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
286 DataTypeController::NOT_RUNNING);
288 DataTypeManager::ConfigureResult expected_result_done(
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));
297 model_association_manager.StopDisabledTypes();
298 model_association_manager.StartAssociationAsync(types);
300 GetController(controllers_,
301 syncer::BOOKMARKS)->SimulateModelLoadFinishing();
302 GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
303 DataTypeController::OK);
306 // Start 2 types. One of which timeout loading. Ensure that type is
307 // fully configured eventually.
308 TEST_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_,
316 syncer::ModelTypeSet types;
317 types.Put(syncer::BOOKMARKS);
318 types.Put(syncer::APPS);
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,
325 std::map<syncer::ModelType, syncer::SyncError>(),
326 expected_types_waiting_to_load,
327 syncer::ModelTypeSet());
329 DataTypeManager::ConfigureResult expected_result_done(
332 std::map<syncer::ModelType, syncer::SyncError>(),
333 syncer::ModelTypeSet(),
334 syncer::ModelTypeSet());
336 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
337 WillOnce(VerifyResult(expected_result_partially_done));
338 EXPECT_CALL(result_processor_, OnTypesLoaded());
340 model_association_manager.Initialize(types);
341 model_association_manager.StopDisabledTypes();
342 model_association_manager.StartAssociationAsync(types);
344 base::OneShotTimer<ModelAssociationManager>* timer =
345 model_association_manager.GetTimerForTesting();
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();
354 // Simulate delayed loading of bookmark model.
355 GetController(controllers_, syncer::APPS)->FinishStart(
356 DataTypeController::OK);
358 GetController(controllers_,
359 syncer::BOOKMARKS)->SimulateModelLoadFinishing();
361 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
362 WillOnce(VerifyResult(expected_result_done));
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);
369 GetController(controllers_,
370 syncer::BOOKMARKS)->SimulateModelLoadFinishing();
372 GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
373 DataTypeController::OK);
376 TEST_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_,
383 syncer::ModelTypeSet types;
384 types.Put(syncer::BOOKMARKS);
385 types.Put(syncer::APPS);
387 DataTypeManager::ConfigureResult result_1st(
389 syncer::ModelTypeSet(syncer::BOOKMARKS),
390 std::map<syncer::ModelType, syncer::SyncError>(),
391 syncer::ModelTypeSet(),
392 syncer::ModelTypeSet());
393 DataTypeManager::ConfigureResult result_2nd(
395 syncer::ModelTypeSet(syncer::APPS),
396 std::map<syncer::ModelType, syncer::SyncError>(),
397 syncer::ModelTypeSet(),
398 syncer::ModelTypeSet());
399 EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
401 WillOnce(VerifyResult(result_1st)).
402 WillOnce(VerifyResult(result_2nd));
404 model_association_manager.Initialize(types);
405 model_association_manager.StopDisabledTypes();
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);
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);
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);
434 } // namespace browser_sync