blob: 554197acbf19ef1fd2468bfc569a290ff3544ec0 [file] [log] [blame]
// 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 "components/sync_driver/ui_data_type_controller.h"
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/tracked_objects.h"
#include "components/sync_driver/data_type_controller_mock.h"
#include "components/sync_driver/fake_generic_change_processor.h"
#include "sync/api/attachments/attachment_service_impl.h"
#include "sync/api/fake_syncable_service.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::InvokeWithoutArgs;
using testing::Return;
namespace browser_sync {
namespace {
// TODO(zea): Expand this to make the dtc type paramterizable. This will let us
// test the basic functionality of all UIDataTypeControllers. We'll need to have
// intelligent default values for the methods queried in the dependent services
// (e.g. those queried in StartModels).
class SyncUIDataTypeControllerTest : public testing::Test,
public SyncApiComponentFactory {
public:
SyncUIDataTypeControllerTest()
: type_(syncer::PREFERENCES),
change_processor_(NULL),
disable_callback_invoked_(false) {}
virtual void SetUp() {
preference_dtc_ =
new UIDataTypeController(
base::MessageLoopProxy::current(),
base::Closure(),
base::Bind(&SyncUIDataTypeControllerTest::DisableTypeCallback,
base::Unretained(this),
type_),
type_,
this);
SetStartExpectations();
}
virtual void TearDown() {
// Must be done before we pump the loop.
syncable_service_.StopSyncing(type_);
preference_dtc_ = NULL;
PumpLoop();
}
virtual base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType(
syncer::ModelType type) OVERRIDE {
return syncable_service_.AsWeakPtr();
}
virtual scoped_ptr<syncer::AttachmentService> CreateAttachmentService(
syncer::AttachmentService::Delegate* delegate) OVERRIDE {
return syncer::AttachmentServiceImpl::CreateForTest();
}
protected:
void SetStartExpectations() {
scoped_ptr<FakeGenericChangeProcessor> p(
new FakeGenericChangeProcessor(this));
change_processor_ = p.get();
scoped_ptr<GenericChangeProcessorFactory> f(
new FakeGenericChangeProcessorFactory(p.Pass()));
preference_dtc_->SetGenericChangeProcessorFactoryForTest(f.Pass());
EXPECT_CALL(model_load_callback_, Run(_, _));
}
void Start() {
preference_dtc_->LoadModels(
base::Bind(&ModelLoadCallbackMock::Run,
base::Unretained(&model_load_callback_)));
preference_dtc_->StartAssociating(
base::Bind(&StartCallbackMock::Run,
base::Unretained(&start_callback_)));
}
void PumpLoop() {
message_loop_.RunUntilIdle();
}
void DisableTypeCallback(syncer::ModelType type,
const tracked_objects::Location& location,
const std::string& message) {
disable_callback_invoked_ = true;
preference_dtc_->Stop();
}
base::MessageLoopForUI message_loop_;
const syncer::ModelType type_;
StartCallbackMock start_callback_;
ModelLoadCallbackMock model_load_callback_;
scoped_refptr<UIDataTypeController> preference_dtc_;
FakeGenericChangeProcessor* change_processor_;
syncer::FakeSyncableService syncable_service_;
bool disable_callback_invoked_;
};
// Start the DTC. Verify that the callback is called with OK, the
// service has been told to start syncing and that the DTC is now in RUNNING
// state.
TEST_F(SyncUIDataTypeControllerTest, Start) {
EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _, _));
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
Start();
EXPECT_EQ(DataTypeController::RUNNING, preference_dtc_->state());
EXPECT_TRUE(syncable_service_.syncing());
}
// Start and then stop the DTC. Verify that the service started and stopped
// syncing, and that the DTC went from RUNNING to NOT_RUNNING.
TEST_F(SyncUIDataTypeControllerTest, StartStop) {
EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _, _));
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
Start();
EXPECT_EQ(DataTypeController::RUNNING, preference_dtc_->state());
EXPECT_TRUE(syncable_service_.syncing());
preference_dtc_->Stop();
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
}
// Start the DTC when no user nodes are created. Verify that the callback
// is called with OK_FIRST_RUN. Stop the DTC.
TEST_F(SyncUIDataTypeControllerTest, StartStopFirstRun) {
EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN, _, _));
change_processor_->set_sync_model_has_user_created_nodes(false);
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
Start();
EXPECT_EQ(DataTypeController::RUNNING, preference_dtc_->state());
EXPECT_TRUE(syncable_service_.syncing());
preference_dtc_->Stop();
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
}
// Start the DTC, but have the service fail association. Verify the callback
// is called with ASSOCIATION_FAILED, the DTC goes to state DISABLED, and the
// service is not syncing. Then stop the DTC.
TEST_F(SyncUIDataTypeControllerTest, StartAssociationFailed) {
EXPECT_CALL(start_callback_,
Run(DataTypeController::ASSOCIATION_FAILED, _, _));
syncable_service_.set_merge_data_and_start_syncing_error(
syncer::SyncError(FROM_HERE,
syncer::SyncError::DATATYPE_ERROR,
"Error",
type_));
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
Start();
EXPECT_EQ(DataTypeController::DISABLED, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
preference_dtc_->Stop();
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
}
// Start the DTC but fail to check if there are user created nodes. Verify the
// DTC calls the callback with UNRECOVERABLE_ERROR and that it goes into
// NOT_RUNNING state. Verify the syncable service is not syncing.
TEST_F(SyncUIDataTypeControllerTest,
StartAssociationTriggersUnrecoverableError) {
EXPECT_CALL(start_callback_,
Run(DataTypeController::UNRECOVERABLE_ERROR, _, _));
change_processor_->set_sync_model_has_user_created_nodes_success(false);
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
Start();
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
}
// Start the DTC, but then trigger an unrecoverable error. Verify the syncer
// gets stopped and the DTC is in NOT_RUNNING state.
TEST_F(SyncUIDataTypeControllerTest, OnSingleDatatypeUnrecoverableError) {
EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _, _));
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_FALSE(syncable_service_.syncing());
Start();
EXPECT_TRUE(syncable_service_.syncing());
preference_dtc_->OnSingleDatatypeUnrecoverableError(FROM_HERE, "Test");
PumpLoop();
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
EXPECT_TRUE(disable_callback_invoked_);
EXPECT_FALSE(syncable_service_.syncing());
}
} // namespace
} // namespace browser_sync