root/components/sync_driver/model_association_manager_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetController
  2. ACTION_P
  3. TEST_F
  4. TEST_F
  5. TEST_F
  6. TEST_F
  7. TEST_F
  8. TEST_F
  9. TEST_F
  10. TEST_F

// Copyright 2014 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 "components/sync_driver/fake_data_type_controller.h"
#include "components/sync_driver/model_association_manager.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));
};

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.unfinished_data_types.Equals(
      expected_result.unfinished_data_types));
}

class SyncModelAssociationManagerTest : public testing::Test {
 public:
  SyncModelAssociationManagerTest() {
  }

 protected:
  base::MessageLoopForUI ui_loop_;
  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);
  controllers_[syncer::APPS] =
      new FakeDataTypeController(syncer::APPS);
  ModelAssociationManager model_association_manager(&controllers_,
                                                    &result_processor_);
  syncer::ModelTypeSet types(syncer::BOOKMARKS, syncer::APPS);
  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));

  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
            DataTypeController::NOT_RUNNING);
  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
            DataTypeController::NOT_RUNNING);

  // Initialize() kicks off model loading.
  model_association_manager.Initialize(types);

  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
            DataTypeController::MODEL_LOADED);
  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
            DataTypeController::MODEL_LOADED);

  model_association_manager.StartAssociationAsync(types);

  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
            DataTypeController::ASSOCIATING);
  EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(),
            DataTypeController::ASSOCIATING);
  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
      DataTypeController::OK);
  GetController(controllers_, syncer::APPS)->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);

  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::ABORTED,
      types,
      errors,
      syncer::ModelTypeSet(syncer::BOOKMARKS),
      syncer::ModelTypeSet());

  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
              WillOnce(VerifyResult(expected_result));

  model_association_manager.Initialize(types);
  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.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.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.StartAssociationAsync(types);

  EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(),
            DataTypeController::ASSOCIATING);
  GetController(controllers_, syncer::BOOKMARKS)->FinishStart(
      DataTypeController::UNRECOVERABLE_ERROR);
}

TEST_F(SyncModelAssociationManagerTest, SlowTypeAsFailedType) {
  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);

  std::map<syncer::ModelType, syncer::SyncError> errors;
  syncer::SyncError error(FROM_HERE,
                          syncer::SyncError::DATATYPE_ERROR,
                          "Association timed out.",
                          syncer::BOOKMARKS);
  errors[syncer::BOOKMARKS] = error;

  syncer::ModelTypeSet expected_types_unfinished;
  expected_types_unfinished.Put(syncer::BOOKMARKS);
  DataTypeManager::ConfigureResult expected_result_partially_done(
      DataTypeManager::PARTIAL_SUCCESS,
      types,
      errors,
      expected_types_unfinished,
      syncer::ModelTypeSet());

  EXPECT_CALL(result_processor_, OnModelAssociationDone(_)).
              WillOnce(VerifyResult(expected_result_partially_done));

  model_association_manager.Initialize(types);
  model_association_manager.StartAssociationAsync(types);
  GetController(controllers_, syncer::APPS)->FinishStart(
      DataTypeController::OK);

  model_association_manager.GetTimerForTesting()->user_task().Run();

  EXPECT_EQ(DataTypeController::NOT_RUNNING,
            GetController(controllers_, syncer::BOOKMARKS)->state());
}

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);

  // 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::MODEL_LOADED);

  // 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::MODEL_LOADED);

  // 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);
}

// Test that model that failed to load between initialization and association
// is reported and stopped properly.
TEST_F(SyncModelAssociationManagerTest, ModelLoadFailBeforeAssociationStart) {
  controllers_[syncer::BOOKMARKS] =
      new FakeDataTypeController(syncer::BOOKMARKS);
  GetController(controllers_, syncer::BOOKMARKS)->SetModelLoadError(
      syncer::SyncError(FROM_HERE, syncer::SyncError::DATATYPE_ERROR,
                        "", 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);
  EXPECT_EQ(DataTypeController::DISABLED,
            GetController(controllers_, syncer::BOOKMARKS)->state());
  model_association_manager.StartAssociationAsync(types);
  EXPECT_EQ(DataTypeController::NOT_RUNNING,
            GetController(controllers_, syncer::BOOKMARKS)->state());
}

}  // namespace browser_sync

/* [<][>][^][v][top][bottom][index][help] */