| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/callback.h" |
| #include "base/message_loop/message_loop.h" |
| #include "chrome/browser/sync/glue/fake_data_type_controller.h" |
| #include "chrome/browser/sync/glue/model_association_manager.h" |
| #include "content/public/test/test_browser_thread.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::_; |
| namespace browser_sync { |
| class MockModelAssociationResultProcessor : |
| public ModelAssociationResultProcessor { |
| public: |
| MockModelAssociationResultProcessor() {} |
| ~MockModelAssociationResultProcessor() {} |
| MOCK_METHOD2(OnSingleDataTypeAssociationDone, |
| void(syncer::ModelType type, |
| const syncer::DataTypeAssociationStats& association_stats)); |
| MOCK_METHOD1(OnModelAssociationDone, void( |
| const DataTypeManager::ConfigureResult& result)); |
| MOCK_METHOD0(OnTypesLoaded, void()); |
| }; |
| |
| FakeDataTypeController* GetController( |
| const DataTypeController::TypeMap& controllers, |
| syncer::ModelType model_type) { |
| DataTypeController::TypeMap::const_iterator it = |
| controllers.find(model_type); |
| if (it == controllers.end()) { |
| return NULL; |
| } |
| return (FakeDataTypeController*)(it->second.get()); |
| } |
| |
| ACTION_P(VerifyResult, expected_result) { |
| EXPECT_EQ(arg0.status, expected_result.status); |
| EXPECT_TRUE(arg0.requested_types.Equals(expected_result.requested_types)); |
| EXPECT_EQ(arg0.failed_data_types.size(), |
| expected_result.failed_data_types.size()); |
| |
| if (arg0.failed_data_types.size() == |
| expected_result.failed_data_types.size()) { |
| std::map<syncer::ModelType, syncer::SyncError>::const_iterator it1, it2; |
| for (it1 = arg0.failed_data_types.begin(), |
| it2 = expected_result.failed_data_types.begin(); |
| it1 != arg0.failed_data_types.end(); |
| ++it1, ++it2) { |
| EXPECT_EQ((*it1).first, (*it2).first); |
| } |
| } |
| |
| EXPECT_TRUE(arg0.waiting_to_start.Equals(expected_result.waiting_to_start)); |
| } |
| |
| class SyncModelAssociationManagerTest : public testing::Test { |
| public: |
| SyncModelAssociationManagerTest() : |
| ui_thread_(content::BrowserThread::UI, &ui_loop_) { |
| } |
| |
| protected: |
| base::MessageLoopForUI ui_loop_; |
| content::TestBrowserThread ui_thread_; |
| MockModelAssociationResultProcessor result_processor_; |
| DataTypeController::TypeMap controllers_; |
| }; |
| |
| // Start a type and make sure ModelAssociationManager callst the |Start| |
| // method and calls the callback when it is done. |
| TEST_F(SyncModelAssociationManagerTest, SimpleModelStart) { |
| controllers_[syncer::BOOKMARKS] = |
| new FakeDataTypeController(syncer::BOOKMARKS); |
| ModelAssociationManager model_association_manager(&controllers_, |
| &result_processor_); |
| syncer::ModelTypeSet types; |
| types.Put(syncer::BOOKMARKS); |
| DataTypeManager::ConfigureResult expected_result( |
| DataTypeManager::OK, |
| types, |
| std::map<syncer::ModelType, syncer::SyncError>(), |
| syncer::ModelTypeSet(), |
| syncer::ModelTypeSet()); |
| EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). |
| WillOnce(VerifyResult(expected_result)); |
| |
| model_association_manager.Initialize(types); |
| model_association_manager.StopDisabledTypes(); |
| model_association_manager.StartAssociationAsync(types); |
| |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::ASSOCIATING); |
| GetController(controllers_, syncer::BOOKMARKS)->FinishStart( |
| DataTypeController::OK); |
| } |
| |
| // Start a type and call stop before it finishes associating. |
| TEST_F(SyncModelAssociationManagerTest, StopModelBeforeFinish) { |
| controllers_[syncer::BOOKMARKS] = |
| new FakeDataTypeController(syncer::BOOKMARKS); |
| ModelAssociationManager model_association_manager( |
| &controllers_, |
| &result_processor_); |
| |
| syncer::ModelTypeSet types; |
| types.Put(syncer::BOOKMARKS); |
| |
| DataTypeManager::ConfigureResult expected_result( |
| DataTypeManager::ABORTED, |
| types, |
| std::map<syncer::ModelType, syncer::SyncError>(), |
| syncer::ModelTypeSet(), |
| syncer::ModelTypeSet()); |
| |
| EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). |
| WillOnce(VerifyResult(expected_result)); |
| |
| model_association_manager.Initialize(types); |
| model_association_manager.StopDisabledTypes(); |
| model_association_manager.StartAssociationAsync(types); |
| |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::ASSOCIATING); |
| model_association_manager.Stop(); |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::NOT_RUNNING); |
| } |
| |
| // Start a type, let it finish and then call stop. |
| TEST_F(SyncModelAssociationManagerTest, StopAfterFinish) { |
| controllers_[syncer::BOOKMARKS] = |
| new FakeDataTypeController(syncer::BOOKMARKS); |
| ModelAssociationManager model_association_manager( |
| &controllers_, |
| &result_processor_); |
| syncer::ModelTypeSet types; |
| types.Put(syncer::BOOKMARKS); |
| DataTypeManager::ConfigureResult expected_result( |
| DataTypeManager::OK, |
| types, |
| std::map<syncer::ModelType, syncer::SyncError>(), |
| syncer::ModelTypeSet(), |
| syncer::ModelTypeSet()); |
| EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). |
| WillOnce(VerifyResult(expected_result)); |
| |
| model_association_manager.Initialize(types); |
| model_association_manager.StopDisabledTypes(); |
| model_association_manager.StartAssociationAsync(types); |
| |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::ASSOCIATING); |
| GetController(controllers_, syncer::BOOKMARKS)->FinishStart( |
| DataTypeController::OK); |
| |
| model_association_manager.Stop(); |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::NOT_RUNNING); |
| } |
| |
| // Make a type fail model association and verify correctness. |
| TEST_F(SyncModelAssociationManagerTest, TypeFailModelAssociation) { |
| controllers_[syncer::BOOKMARKS] = |
| new FakeDataTypeController(syncer::BOOKMARKS); |
| ModelAssociationManager model_association_manager( |
| &controllers_, |
| &result_processor_); |
| syncer::ModelTypeSet types; |
| types.Put(syncer::BOOKMARKS); |
| std::map<syncer::ModelType, syncer::SyncError> errors; |
| syncer::SyncError error(FROM_HERE, |
| syncer::SyncError::DATATYPE_ERROR, |
| "Failed", |
| syncer::BOOKMARKS); |
| errors[syncer::BOOKMARKS] = error; |
| DataTypeManager::ConfigureResult expected_result( |
| DataTypeManager::PARTIAL_SUCCESS, |
| types, |
| errors, |
| syncer::ModelTypeSet(), |
| syncer::ModelTypeSet()); |
| EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). |
| WillOnce(VerifyResult(expected_result)); |
| |
| model_association_manager.Initialize(types); |
| model_association_manager.StopDisabledTypes(); |
| model_association_manager.StartAssociationAsync(types); |
| |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::ASSOCIATING); |
| GetController(controllers_, syncer::BOOKMARKS)->FinishStart( |
| DataTypeController::ASSOCIATION_FAILED); |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::NOT_RUNNING); |
| } |
| |
| // Ensure configuring stops when a type returns a unrecoverable error. |
| TEST_F(SyncModelAssociationManagerTest, TypeReturnUnrecoverableError) { |
| controllers_[syncer::BOOKMARKS] = |
| new FakeDataTypeController(syncer::BOOKMARKS); |
| ModelAssociationManager model_association_manager( |
| &controllers_, |
| &result_processor_); |
| syncer::ModelTypeSet types; |
| types.Put(syncer::BOOKMARKS); |
| std::map<syncer::ModelType, syncer::SyncError> errors; |
| syncer::SyncError error(FROM_HERE, |
| syncer::SyncError::DATATYPE_ERROR, |
| "Failed", |
| syncer::BOOKMARKS); |
| errors[syncer::BOOKMARKS] = error; |
| DataTypeManager::ConfigureResult expected_result( |
| DataTypeManager::UNRECOVERABLE_ERROR, |
| types, |
| errors, |
| syncer::ModelTypeSet(), |
| syncer::ModelTypeSet()); |
| EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). |
| WillOnce(VerifyResult(expected_result)); |
| |
| model_association_manager.Initialize(types); |
| model_association_manager.StopDisabledTypes(); |
| model_association_manager.StartAssociationAsync(types); |
| |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::ASSOCIATING); |
| GetController(controllers_, syncer::BOOKMARKS)->FinishStart( |
| DataTypeController::UNRECOVERABLE_ERROR); |
| } |
| |
| TEST_F(SyncModelAssociationManagerTest, InitializeAbortsLoad) { |
| controllers_[syncer::BOOKMARKS] = |
| new FakeDataTypeController(syncer::BOOKMARKS); |
| controllers_[syncer::THEMES] = |
| new FakeDataTypeController(syncer::THEMES); |
| |
| GetController(controllers_, syncer::BOOKMARKS)->SetDelayModelLoad(); |
| ModelAssociationManager model_association_manager(&controllers_, |
| &result_processor_); |
| syncer::ModelTypeSet types(syncer::BOOKMARKS, syncer::THEMES); |
| |
| syncer::ModelTypeSet expected_types_waiting_to_load; |
| expected_types_waiting_to_load.Put(syncer::BOOKMARKS); |
| DataTypeManager::ConfigureResult expected_result_partially_done( |
| DataTypeManager::PARTIAL_SUCCESS, |
| types, |
| std::map<syncer::ModelType, syncer::SyncError>(), |
| expected_types_waiting_to_load, |
| syncer::ModelTypeSet()); |
| |
| model_association_manager.Initialize(types); |
| model_association_manager.StopDisabledTypes(); |
| |
| model_association_manager.StartAssociationAsync(types); |
| |
| EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). |
| WillOnce(VerifyResult(expected_result_partially_done)); |
| |
| base::OneShotTimer<ModelAssociationManager>* timer = |
| model_association_manager.GetTimerForTesting(); |
| |
| base::Closure task = timer->user_task(); |
| timer->Stop(); |
| task.Run(); // Bookmark load times out here. |
| |
| // Apps finishes associating here. |
| GetController(controllers_, syncer::THEMES)->FinishStart( |
| DataTypeController::OK); |
| |
| // At this point, BOOKMARKS is still waiting to load (as evidenced by |
| // expected_result_partially_done). If we schedule another Initialize (which |
| // could happen in practice due to reconfiguration), this should abort |
| // BOOKMARKS. Aborting will call ModelLoadCallback, but the |
| // ModelAssociationManager should be smart enough to know that this is not due |
| // to the type having completed loading. |
| EXPECT_CALL(result_processor_, OnTypesLoaded()).Times(0); |
| |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::MODEL_STARTING); |
| |
| model_association_manager.Initialize(types); |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::NOT_RUNNING); |
| |
| DataTypeManager::ConfigureResult expected_result_done( |
| DataTypeManager::OK, |
| types, |
| std::map<syncer::ModelType, syncer::SyncError>(), |
| syncer::ModelTypeSet(), |
| syncer::ModelTypeSet()); |
| EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). |
| WillOnce(VerifyResult(expected_result_done)); |
| |
| model_association_manager.StopDisabledTypes(); |
| model_association_manager.StartAssociationAsync(types); |
| |
| GetController(controllers_, |
| syncer::BOOKMARKS)->SimulateModelLoadFinishing(); |
| GetController(controllers_, syncer::BOOKMARKS)->FinishStart( |
| DataTypeController::OK); |
| } |
| |
| // Start 2 types. One of which timeout loading. Ensure that type is |
| // fully configured eventually. |
| TEST_F(SyncModelAssociationManagerTest, ModelStartWithSlowLoadingType) { |
| controllers_[syncer::BOOKMARKS] = |
| new FakeDataTypeController(syncer::BOOKMARKS); |
| controllers_[syncer::APPS] = |
| new FakeDataTypeController(syncer::APPS); |
| GetController(controllers_, syncer::BOOKMARKS)->SetDelayModelLoad(); |
| ModelAssociationManager model_association_manager(&controllers_, |
| &result_processor_); |
| syncer::ModelTypeSet types; |
| types.Put(syncer::BOOKMARKS); |
| types.Put(syncer::APPS); |
| |
| syncer::ModelTypeSet expected_types_waiting_to_load; |
| expected_types_waiting_to_load.Put(syncer::BOOKMARKS); |
| DataTypeManager::ConfigureResult expected_result_partially_done( |
| DataTypeManager::PARTIAL_SUCCESS, |
| types, |
| std::map<syncer::ModelType, syncer::SyncError>(), |
| expected_types_waiting_to_load, |
| syncer::ModelTypeSet()); |
| |
| DataTypeManager::ConfigureResult expected_result_done( |
| DataTypeManager::OK, |
| types, |
| std::map<syncer::ModelType, syncer::SyncError>(), |
| syncer::ModelTypeSet(), |
| syncer::ModelTypeSet()); |
| |
| EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). |
| WillOnce(VerifyResult(expected_result_partially_done)); |
| EXPECT_CALL(result_processor_, OnTypesLoaded()); |
| |
| model_association_manager.Initialize(types); |
| model_association_manager.StopDisabledTypes(); |
| model_association_manager.StartAssociationAsync(types); |
| |
| base::OneShotTimer<ModelAssociationManager>* timer = |
| model_association_manager.GetTimerForTesting(); |
| |
| // Note: Independent of the timeout value this test is not flaky. |
| // The reason is timer posts a task which would never be executed |
| // as we dont let the message loop run. |
| base::Closure task = timer->user_task(); |
| timer->Stop(); |
| task.Run(); |
| |
| // Simulate delayed loading of bookmark model. |
| GetController(controllers_, syncer::APPS)->FinishStart( |
| DataTypeController::OK); |
| |
| GetController(controllers_, |
| syncer::BOOKMARKS)->SimulateModelLoadFinishing(); |
| |
| EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). |
| WillOnce(VerifyResult(expected_result_done)); |
| |
| // Do it once more to associate bookmarks. |
| model_association_manager.Initialize(types); |
| model_association_manager.StopDisabledTypes(); |
| model_association_manager.StartAssociationAsync(types); |
| |
| GetController(controllers_, |
| syncer::BOOKMARKS)->SimulateModelLoadFinishing(); |
| |
| GetController(controllers_, syncer::BOOKMARKS)->FinishStart( |
| DataTypeController::OK); |
| } |
| |
| TEST_F(SyncModelAssociationManagerTest, StartMultipleTimes) { |
| controllers_[syncer::BOOKMARKS] = |
| new FakeDataTypeController(syncer::BOOKMARKS); |
| controllers_[syncer::APPS] = |
| new FakeDataTypeController(syncer::APPS); |
| ModelAssociationManager model_association_manager(&controllers_, |
| &result_processor_); |
| syncer::ModelTypeSet types; |
| types.Put(syncer::BOOKMARKS); |
| types.Put(syncer::APPS); |
| |
| DataTypeManager::ConfigureResult result_1st( |
| DataTypeManager::OK, |
| syncer::ModelTypeSet(syncer::BOOKMARKS), |
| std::map<syncer::ModelType, syncer::SyncError>(), |
| syncer::ModelTypeSet(), |
| syncer::ModelTypeSet()); |
| DataTypeManager::ConfigureResult result_2nd( |
| DataTypeManager::OK, |
| syncer::ModelTypeSet(syncer::APPS), |
| std::map<syncer::ModelType, syncer::SyncError>(), |
| syncer::ModelTypeSet(), |
| syncer::ModelTypeSet()); |
| EXPECT_CALL(result_processor_, OnModelAssociationDone(_)). |
| Times(2). |
| WillOnce(VerifyResult(result_1st)). |
| WillOnce(VerifyResult(result_2nd)); |
| |
| model_association_manager.Initialize(types); |
| model_association_manager.StopDisabledTypes(); |
| |
| // Start BOOKMARKS first. |
| model_association_manager.StartAssociationAsync( |
| syncer::ModelTypeSet(syncer::BOOKMARKS)); |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::ASSOCIATING); |
| EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(), |
| DataTypeController::NOT_RUNNING); |
| |
| // Finish BOOKMARKS association. |
| GetController(controllers_, syncer::BOOKMARKS)->FinishStart( |
| DataTypeController::OK); |
| EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), |
| DataTypeController::RUNNING); |
| EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(), |
| DataTypeController::NOT_RUNNING); |
| |
| // Start APPS next. |
| model_association_manager.StartAssociationAsync( |
| syncer::ModelTypeSet(syncer::APPS)); |
| EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(), |
| DataTypeController::ASSOCIATING); |
| GetController(controllers_, syncer::APPS)->FinishStart( |
| DataTypeController::OK); |
| EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(), |
| DataTypeController::RUNNING); |
| } |
| |
| } // namespace browser_sync |