blob: 2c707443d961de1f1b92b071ca7d4acab71d35e9 [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/basictypes.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/invalidation/fake_invalidation_service.h"
#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h"
#include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/sync/glue/sync_backend_host_mock.h"
#include "chrome/browser/sync/managed_user_signin_manager_wrapper.h"
#include "chrome/browser/sync/profile_sync_components_factory_mock.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_pref_service_syncable.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/invalidation/invalidation_service.h"
#include "components/invalidation/profile_invalidation_provider.h"
#include "components/signin/core/browser/signin_manager.h"
#include "components/sync_driver/data_type_manager_impl.h"
#include "components/sync_driver/pref_names.h"
#include "components/sync_driver/sync_prefs.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "google_apis/gaia/gaia_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
class BrowserContext;
}
namespace browser_sync {
namespace {
ACTION(ReturnNewDataTypeManager) {
return new browser_sync::DataTypeManagerImpl(base::Closure(),
arg0,
arg1,
arg2,
arg3,
arg4,
arg5);
}
using testing::Return;
using testing::StrictMock;
using testing::_;
class TestProfileSyncServiceObserver : public ProfileSyncServiceObserver {
public:
explicit TestProfileSyncServiceObserver(ProfileSyncService* service)
: service_(service), first_setup_in_progress_(false) {}
virtual void OnStateChanged() OVERRIDE {
first_setup_in_progress_ = service_->FirstSetupInProgress();
}
bool first_setup_in_progress() const { return first_setup_in_progress_; }
private:
ProfileSyncService* service_;
bool first_setup_in_progress_;
};
// A variant of the SyncBackendHostMock that won't automatically
// call back when asked to initialized. Allows us to test things
// that could happen while backend init is in progress.
class SyncBackendHostNoReturn : public SyncBackendHostMock {
virtual void Initialize(
SyncFrontend* frontend,
scoped_ptr<base::Thread> sync_thread,
const syncer::WeakHandle<syncer::JsEventHandler>& event_handler,
const GURL& service_url,
const syncer::SyncCredentials& credentials,
bool delete_sync_data_folder,
scoped_ptr<syncer::SyncManagerFactory> sync_manager_factory,
scoped_ptr<syncer::UnrecoverableErrorHandler> unrecoverable_error_handler,
syncer::ReportUnrecoverableErrorFunction
report_unrecoverable_error_function,
syncer::NetworkResources* network_resources) OVERRIDE {}
};
class SyncBackendHostMockCollectDeleteDirParam : public SyncBackendHostMock {
public:
explicit SyncBackendHostMockCollectDeleteDirParam(
std::vector<bool>* delete_dir_param)
: delete_dir_param_(delete_dir_param) {}
virtual void Initialize(
SyncFrontend* frontend,
scoped_ptr<base::Thread> sync_thread,
const syncer::WeakHandle<syncer::JsEventHandler>& event_handler,
const GURL& service_url,
const syncer::SyncCredentials& credentials,
bool delete_sync_data_folder,
scoped_ptr<syncer::SyncManagerFactory> sync_manager_factory,
scoped_ptr<syncer::UnrecoverableErrorHandler> unrecoverable_error_handler,
syncer::ReportUnrecoverableErrorFunction
report_unrecoverable_error_function,
syncer::NetworkResources* network_resources) OVERRIDE {
delete_dir_param_->push_back(delete_sync_data_folder);
SyncBackendHostMock::Initialize(frontend, sync_thread.Pass(),
event_handler, service_url, credentials,
delete_sync_data_folder,
sync_manager_factory.Pass(),
unrecoverable_error_handler.Pass(),
report_unrecoverable_error_function,
network_resources);
}
private:
std::vector<bool>* delete_dir_param_;
};
ACTION(ReturnNewSyncBackendHostMock) {
return new browser_sync::SyncBackendHostMock();
}
ACTION(ReturnNewSyncBackendHostNoReturn) {
return new browser_sync::SyncBackendHostNoReturn();
}
ACTION_P(ReturnNewMockHostCollectDeleteDirParam, delete_dir_param) {
return new browser_sync::SyncBackendHostMockCollectDeleteDirParam(
delete_dir_param);
}
KeyedService* BuildFakeProfileInvalidationProvider(
content::BrowserContext* context) {
return new invalidation::ProfileInvalidationProvider(
scoped_ptr<invalidation::InvalidationService>(
new invalidation::FakeInvalidationService));
}
// A test harness that uses a real ProfileSyncService and in most cases a
// MockSyncBackendHost.
//
// This is useful if we want to test the ProfileSyncService and don't care about
// testing the SyncBackendHost.
class ProfileSyncServiceTest : public ::testing::Test {
protected:
ProfileSyncServiceTest()
: thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
profile_manager_(TestingBrowserProcess::GetGlobal()) {}
virtual ~ProfileSyncServiceTest() {}
virtual void SetUp() OVERRIDE {
CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kSyncDeferredStartupTimeoutSeconds, "0");
CHECK(profile_manager_.SetUp());
TestingProfile::TestingFactories testing_facotries;
testing_facotries.push_back(
std::make_pair(ProfileOAuth2TokenServiceFactory::GetInstance(),
BuildAutoIssuingFakeProfileOAuth2TokenService));
testing_facotries.push_back(
std::make_pair(
invalidation::ProfileInvalidationProviderFactory::GetInstance(),
BuildFakeProfileInvalidationProvider));
profile_ = profile_manager_.CreateTestingProfile(
"sync-service-test", scoped_ptr<PrefServiceSyncable>(),
base::UTF8ToUTF16("sync-service-test"), 0, std::string(),
testing_facotries);
}
virtual void TearDown() OVERRIDE {
// Kill the service before the profile.
if (service_)
service_->Shutdown();
service_.reset();
}
void IssueTestTokens() {
ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
->UpdateCredentials("test", "oauth2_login_token");
}
void CreateService(ProfileSyncServiceStartBehavior behavior) {
SigninManagerBase* signin =
SigninManagerFactory::GetForProfile(profile_);
signin->SetAuthenticatedUsername("test");
ProfileOAuth2TokenService* oauth2_token_service =
ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
components_factory_ = new StrictMock<ProfileSyncComponentsFactoryMock>();
service_.reset(new ProfileSyncService(
components_factory_,
profile_,
make_scoped_ptr(new ManagedUserSigninManagerWrapper(profile_, signin)),
oauth2_token_service,
behavior));
service_->SetClearingBrowseringDataForTesting(
base::Bind(&ProfileSyncServiceTest::ClearBrowsingDataCallback,
base::Unretained(this)));
}
#if defined(OS_WIN) || defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
void CreateServiceWithoutSignIn() {
CreateService(browser_sync::MANUAL_START);
SigninManagerFactory::GetForProfile(profile())->SignOut(
signin_metrics::SIGNOUT_TEST);
service()->SetBackupStartDelayForTest(
base::TimeDelta::FromMilliseconds(100));
}
#endif
void ShutdownAndDeleteService() {
if (service_)
service_->Shutdown();
service_.reset();
}
void Initialize() {
service_->Initialize();
}
void ExpectDataTypeManagerCreation(int times) {
EXPECT_CALL(*components_factory_, CreateDataTypeManager(_, _, _, _, _, _))
.Times(times)
.WillRepeatedly(ReturnNewDataTypeManager());
}
void ExpectSyncBackendHostCreation(int times) {
EXPECT_CALL(*components_factory_, CreateSyncBackendHost(_, _, _, _, _))
.Times(times)
.WillRepeatedly(ReturnNewSyncBackendHostMock());
}
void ExpectSyncBackendHostCreationCollectDeleteDir(
int times, std::vector<bool> *delete_dir_param) {
EXPECT_CALL(*components_factory_, CreateSyncBackendHost(_, _, _, _, _))
.Times(times)
.WillRepeatedly(ReturnNewMockHostCollectDeleteDirParam(
delete_dir_param));
}
void PrepareDelayedInitSyncBackendHost() {
EXPECT_CALL(*components_factory_, CreateSyncBackendHost(_, _, _, _, _)).
WillOnce(ReturnNewSyncBackendHostNoReturn());
}
TestingProfile* profile() {
return profile_;
}
ProfileSyncService* service() {
return service_.get();
}
ProfileSyncComponentsFactoryMock* components_factory() {
return components_factory_;
}
void ClearBrowsingDataCallback(Profile* profile, base::Time start,
base::Time end) {
EXPECT_EQ(profile_, profile);
clear_browsing_data_start_ = start;
}
protected:
// The requested start time when ClearBrowsingDataCallback is called.
base::Time clear_browsing_data_start_;
private:
content::TestBrowserThreadBundle thread_bundle_;
TestingProfileManager profile_manager_;
TestingProfile* profile_;
scoped_ptr<ProfileSyncService> service_;
// Pointer to the components factory. Not owned. May be null.
ProfileSyncComponentsFactoryMock* components_factory_;
};
// Verify that the server URLs are sane.
TEST_F(ProfileSyncServiceTest, InitialState) {
CreateService(browser_sync::AUTO_START);
Initialize();
const std::string& url = service()->sync_service_url().spec();
EXPECT_TRUE(url == ProfileSyncService::kSyncServerUrl ||
url == ProfileSyncService::kDevServerUrl);
}
// Verify a successful initialization.
TEST_F(ProfileSyncServiceTest, SuccessfulInitialization) {
profile()->GetTestingPrefService()->SetManagedPref(
sync_driver::prefs::kSyncManaged, base::Value::CreateBooleanValue(false));
IssueTestTokens();
CreateService(browser_sync::AUTO_START);
ExpectDataTypeManagerCreation(1);
ExpectSyncBackendHostCreation(1);
Initialize();
EXPECT_FALSE(service()->IsManaged());
EXPECT_TRUE(service()->sync_initialized());
EXPECT_EQ(ProfileSyncService::SYNC, service()->backend_mode());
}
// Verify that the SetSetupInProgress function call updates state
// and notifies observers.
TEST_F(ProfileSyncServiceTest, SetupInProgress) {
CreateService(browser_sync::AUTO_START);
Initialize();
TestProfileSyncServiceObserver observer(service());
service()->AddObserver(&observer);
service()->SetSetupInProgress(true);
EXPECT_TRUE(observer.first_setup_in_progress());
service()->SetSetupInProgress(false);
EXPECT_FALSE(observer.first_setup_in_progress());
service()->RemoveObserver(&observer);
}
// Verify that disable by enterprise policy works.
TEST_F(ProfileSyncServiceTest, DisabledByPolicyBeforeInit) {
profile()->GetTestingPrefService()->SetManagedPref(
sync_driver::prefs::kSyncManaged, base::Value::CreateBooleanValue(true));
IssueTestTokens();
CreateService(browser_sync::AUTO_START);
Initialize();
EXPECT_TRUE(service()->IsManaged());
EXPECT_FALSE(service()->sync_initialized());
}
// Verify that disable by enterprise policy works even after the backend has
// been initialized.
TEST_F(ProfileSyncServiceTest, DisabledByPolicyAfterInit) {
IssueTestTokens();
CreateService(browser_sync::AUTO_START);
ExpectDataTypeManagerCreation(1);
ExpectSyncBackendHostCreation(1);
Initialize();
EXPECT_FALSE(service()->IsManaged());
EXPECT_TRUE(service()->sync_initialized());
profile()->GetTestingPrefService()->SetManagedPref(
sync_driver::prefs::kSyncManaged, base::Value::CreateBooleanValue(true));
EXPECT_TRUE(service()->IsManaged());
EXPECT_FALSE(service()->sync_initialized());
}
// Exercies the ProfileSyncService's code paths related to getting shut down
// before the backend initialize call returns.
TEST_F(ProfileSyncServiceTest, AbortedByShutdown) {
CreateService(browser_sync::AUTO_START);
PrepareDelayedInitSyncBackendHost();
IssueTestTokens();
Initialize();
EXPECT_FALSE(service()->sync_initialized());
ShutdownAndDeleteService();
}
// Test StopAndSuppress() before we've initialized the backend.
TEST_F(ProfileSyncServiceTest, EarlyStopAndSuppress) {
CreateService(browser_sync::AUTO_START);
IssueTestTokens();
service()->StopAndSuppress();
EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(
sync_driver::prefs::kSyncSuppressStart));
// Because of supression, this should fail.
Initialize();
EXPECT_FALSE(service()->sync_initialized());
// Remove suppression. This should be enough to allow init to happen.
ExpectDataTypeManagerCreation(1);
ExpectSyncBackendHostCreation(1);
service()->UnsuppressAndStart();
EXPECT_TRUE(service()->sync_initialized());
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(
sync_driver::prefs::kSyncSuppressStart));
}
// Test StopAndSuppress() after we've initialized the backend.
TEST_F(ProfileSyncServiceTest, DisableAndEnableSyncTemporarily) {
CreateService(browser_sync::AUTO_START);
IssueTestTokens();
ExpectDataTypeManagerCreation(1);
ExpectSyncBackendHostCreation(1);
Initialize();
EXPECT_TRUE(service()->sync_initialized());
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(
sync_driver::prefs::kSyncSuppressStart));
testing::Mock::VerifyAndClearExpectations(components_factory());
service()->StopAndSuppress();
EXPECT_FALSE(service()->sync_initialized());
EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(
sync_driver::prefs::kSyncSuppressStart));
ExpectDataTypeManagerCreation(1);
ExpectSyncBackendHostCreation(1);
service()->UnsuppressAndStart();
EXPECT_TRUE(service()->sync_initialized());
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(
sync_driver::prefs::kSyncSuppressStart));
}
// Certain ProfileSyncService tests don't apply to Chrome OS, for example
// things that deal with concepts like "signing out" and policy.
#if defined(OS_WIN) || defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
TEST_F(ProfileSyncServiceTest, EnableSyncAndSignOutDesktop) {
CreateService(browser_sync::AUTO_START);
ExpectDataTypeManagerCreation(2);
ExpectSyncBackendHostCreation(2);
IssueTestTokens();
Initialize();
EXPECT_TRUE(service()->sync_initialized());
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(
sync_driver::prefs::kSyncSuppressStart));
EXPECT_EQ(ProfileSyncService::SYNC, service()->backend_mode());
SigninManagerFactory::GetForProfile(profile())->SignOut(
signin_metrics::SIGNOUT_TEST);
EXPECT_TRUE(service()->sync_initialized());
EXPECT_EQ(ProfileSyncService::BACKUP, service()->backend_mode());
}
#elif !defined (OS_CHROMEOS)
TEST_F(ProfileSyncServiceTest, EnableSyncAndSignOut) {
CreateService(browser_sync::AUTO_START);
ExpectDataTypeManagerCreation(1);
ExpectSyncBackendHostCreation(1);
IssueTestTokens();
Initialize();
EXPECT_TRUE(service()->sync_initialized());
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(
sync_driver::prefs::kSyncSuppressStart));
SigninManagerFactory::GetForProfile(profile())->SignOut(
signin_metrics::SIGNOUT_TEST);
EXPECT_FALSE(service()->sync_initialized());
}
#endif // !defined(OS_CHROMEOS)
TEST_F(ProfileSyncServiceTest, GetSyncTokenStatus) {
CreateService(browser_sync::AUTO_START);
IssueTestTokens();
ExpectDataTypeManagerCreation(1);
ExpectSyncBackendHostCreation(1);
Initialize();
// Initial status.
ProfileSyncService::SyncTokenStatus token_status =
service()->GetSyncTokenStatus();
EXPECT_EQ(syncer::CONNECTION_NOT_ATTEMPTED, token_status.connection_status);
EXPECT_TRUE(token_status.connection_status_update_time.is_null());
EXPECT_TRUE(token_status.token_request_time.is_null());
EXPECT_TRUE(token_status.token_receive_time.is_null());
// Simulate an auth error.
service()->OnConnectionStatusChange(syncer::CONNECTION_AUTH_ERROR);
// The token request will take the form of a posted task. Run it.
base::RunLoop loop;
loop.RunUntilIdle();
token_status = service()->GetSyncTokenStatus();
EXPECT_EQ(syncer::CONNECTION_AUTH_ERROR, token_status.connection_status);
EXPECT_FALSE(token_status.connection_status_update_time.is_null());
EXPECT_FALSE(token_status.token_request_time.is_null());
EXPECT_FALSE(token_status.token_receive_time.is_null());
EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
token_status.last_get_token_error);
EXPECT_TRUE(token_status.next_token_request_time.is_null());
// Simulate successful connection.
service()->OnConnectionStatusChange(syncer::CONNECTION_OK);
token_status = service()->GetSyncTokenStatus();
EXPECT_EQ(syncer::CONNECTION_OK, token_status.connection_status);
}
#if defined(ENABLE_PRE_SYNC_BACKUP)
void QuitLoop() {
base::MessageLoop::current()->Quit();
}
TEST_F(ProfileSyncServiceTest, StartBackup) {
CreateServiceWithoutSignIn();
ExpectDataTypeManagerCreation(1);
std::vector<bool> delete_dir_param;
ExpectSyncBackendHostCreationCollectDeleteDir(1, &delete_dir_param);
Initialize();
EXPECT_EQ(ProfileSyncService::IDLE, service()->backend_mode());
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE, base::Bind(&QuitLoop),
base::TimeDelta::FromMilliseconds(100));
base::MessageLoop::current()->Run();
EXPECT_EQ(ProfileSyncService::BACKUP, service()->backend_mode());
EXPECT_EQ(1u, delete_dir_param.size());
EXPECT_FALSE(delete_dir_param[0]);
}
TEST_F(ProfileSyncServiceTest, BackupAfterSyncDisabled) {
CreateService(browser_sync::MANUAL_START);
service()->SetSyncSetupCompleted();
ExpectDataTypeManagerCreation(2);
std::vector<bool> delete_dir_param;
ExpectSyncBackendHostCreationCollectDeleteDir(2, &delete_dir_param);
IssueTestTokens();
Initialize();
base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&QuitLoop));
EXPECT_TRUE(service()->sync_initialized());
EXPECT_EQ(ProfileSyncService::SYNC, service()->backend_mode());
// First sync time should be recorded.
sync_driver::SyncPrefs sync_prefs(service()->profile()->GetPrefs());
EXPECT_FALSE(sync_prefs.GetFirstSyncTime().is_null());
syncer::SyncProtocolError client_cmd;
client_cmd.action = syncer::DISABLE_SYNC_ON_CLIENT;
service()->OnActionableError(client_cmd);
EXPECT_EQ(ProfileSyncService::BACKUP, service()->backend_mode());
// Browsing data is not cleared because rollback is skipped.
EXPECT_TRUE(clear_browsing_data_start_.is_null());
// First sync time is erased once backup starts.
EXPECT_TRUE(sync_prefs.GetFirstSyncTime().is_null());
EXPECT_EQ(2u, delete_dir_param.size());
EXPECT_FALSE(delete_dir_param[0]);
EXPECT_TRUE(delete_dir_param[1]);
}
TEST_F(ProfileSyncServiceTest, RollbackThenBackup) {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kSyncEnableRollback);
CreateService(browser_sync::MANUAL_START);
service()->SetSyncSetupCompleted();
ExpectDataTypeManagerCreation(3);
std::vector<bool> delete_dir_param;
ExpectSyncBackendHostCreationCollectDeleteDir(3, &delete_dir_param);
IssueTestTokens();
Initialize();
base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&QuitLoop));
EXPECT_TRUE(service()->sync_initialized());
EXPECT_EQ(ProfileSyncService::SYNC, service()->backend_mode());
// First sync time should be recorded.
sync_driver::SyncPrefs sync_prefs(service()->profile()->GetPrefs());
base::Time first_sync_time = sync_prefs.GetFirstSyncTime();
EXPECT_FALSE(first_sync_time.is_null());
syncer::SyncProtocolError client_cmd;
client_cmd.action = syncer::DISABLE_SYNC_AND_ROLLBACK;
service()->OnActionableError(client_cmd);
EXPECT_TRUE(service()->sync_initialized());
EXPECT_EQ(ProfileSyncService::ROLLBACK, service()->backend_mode());
// Browser data should be cleared during rollback.
EXPECT_EQ(first_sync_time, clear_browsing_data_start_);
client_cmd.action = syncer::ROLLBACK_DONE;
service()->OnActionableError(client_cmd);
EXPECT_TRUE(service()->sync_initialized());
EXPECT_EQ(ProfileSyncService::BACKUP, service()->backend_mode());
// First sync time is erased once backup starts.
EXPECT_TRUE(sync_prefs.GetFirstSyncTime().is_null());
EXPECT_EQ(3u, delete_dir_param.size());
EXPECT_FALSE(delete_dir_param[0]);
EXPECT_FALSE(delete_dir_param[1]);
EXPECT_TRUE(delete_dir_param[2]);
}
#endif
TEST_F(ProfileSyncServiceTest, GetSyncServiceURL) {
// See that we can override the URL with a flag.
CommandLine command_line(
base::FilePath(base::FilePath(FILE_PATH_LITERAL("chrome.exe"))));
command_line.AppendSwitchASCII(switches::kSyncServiceURL, "https://foo/bar");
EXPECT_EQ("https://foo/bar",
ProfileSyncService::GetSyncServiceURL(command_line).spec());
}
} // namespace
} // namespace browser_sync