blob: a4abfea5d9d6f2c58c189cc13437b3d5e2a48968 [file] [log] [blame]
// 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