blob: dd25d23d81a427270cec56559df6f81bf0fb4063 [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 "chrome/browser/sync/sync_global_error.h"
#include "base/basictypes.h"
#include "base/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/signin/signin_manager.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/sync/profile_sync_service_mock.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gmock/include/gmock/gmock-actions.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::_;
using content::BrowserThread;
namespace {
#if 0
// TODO(altimofeev) See below.
class BrowserMock: public Browser {
public:
explicit BrowserMock(Type type, Profile* profile) : Browser(type, profile) {}
MOCK_METHOD2(ExecuteCommandWithDisposition,
void(int command_id, WindowOpenDisposition));
};
#endif
class LoginUIServiceMock: public LoginUIService {
public:
explicit LoginUIServiceMock() : LoginUIService() {}
MOCK_METHOD1(ShowLoginUI, void(Browser*));
};
ProfileKeyedService* BuildMockLoginUIService(Profile* profile) {
return new LoginUIServiceMock();
}
// Same as BrowserWithTestWindowTest, but uses MockBrowser to test calls to
// ExecuteCommand method.
class SyncGlobalErrorTest : public BrowserWithTestWindowTest {
public:
SyncGlobalErrorTest() {}
virtual ~SyncGlobalErrorTest() {}
#if 0
// TODO(altimofeev): see below.
virtual void SetUp() OVERRIDE {
testing::Test::SetUp();
set_profile(CreateProfile());
set_browser(new BrowserMock(Browser::TYPE_TABBED, profile()));
set_window(new TestBrowserWindow(browser()));
browser()->SetWindowForTesting(window());
}
virtual void TearDown() OVERRIDE {
testing::Test::TearDown();
}
#endif
private:
DISALLOW_COPY_AND_ASSIGN(SyncGlobalErrorTest);
};
// Utility function to test that SyncGlobalError behaves correct for the given
// error condition.
void VerifySyncGlobalErrorResult(NiceMock<ProfileSyncServiceMock>* service,
LoginUIServiceMock* login_ui_service,
Browser* browser,
SyncGlobalError* error,
GoogleServiceAuthError::State error_state,
bool is_signed_in,
bool is_error) {
EXPECT_CALL(*service, HasSyncSetupCompleted())
.WillRepeatedly(Return(is_signed_in));
GoogleServiceAuthError auth_error(error_state);
EXPECT_CALL(*service, GetAuthError()).WillRepeatedly(ReturnRef(auth_error));
error->OnStateChanged();
// If there is an error then a wrench button badge, menu item, and bubble view
// should be shown.
EXPECT_EQ(error->HasBadge(), is_error);
EXPECT_EQ(error->HasMenuItem() || error->HasCustomizedSyncMenuItem(),
is_error);
EXPECT_EQ(error->HasBubbleView(), is_error);
// If there is an error then labels should not be empty.
EXPECT_NE(error->MenuItemCommandID(), 0);
EXPECT_NE(error->MenuItemLabel().empty(), is_error);
EXPECT_NE(error->GetBubbleViewAcceptButtonLabel().empty(), is_error);
// We never have a cancel button.
EXPECT_TRUE(error->GetBubbleViewCancelButtonLabel().empty());
// We always return a hardcoded title.
EXPECT_FALSE(error->GetBubbleViewTitle().empty());
#if defined(OS_CHROMEOS)
// TODO(altimofeev): Implement this in a way that doesn't involve subclassing
// Browser or using GMock on browser/ui types which is
// banned. Consider observing NOTIFICATION_APP_TERMINATING
// instead.
// http://crbug.com/134675
#else
#if defined(OS_CHROMEOS)
if (error_state != GoogleServiceAuthError::NONE) {
// In CrOS sign-in/sign-out is made to fix the error.
EXPECT_CALL(*static_cast<BrowserMock*>(browser),
ExecuteCommandWithDisposition(IDC_EXIT, _));
error->ExecuteMenuItem(browser);
}
#else
// Test message handler.
if (is_error) {
EXPECT_CALL(*login_ui_service, ShowLoginUI(browser));
error->ExecuteMenuItem(browser);
EXPECT_CALL(*login_ui_service, ShowLoginUI(browser));
error->BubbleViewAcceptButtonPressed(browser);
error->BubbleViewDidClose(browser);
}
#endif
#endif
}
} // namespace
// Test that SyncGlobalError shows an error if a passphrase is required.
TEST_F(SyncGlobalErrorTest, PassphraseGlobalError) {
scoped_ptr<Profile> profile(
ProfileSyncServiceMock::MakeSignedInTestingProfile());
NiceMock<ProfileSyncServiceMock> service(profile.get());
SigninManager* signin = SigninManagerFactory::GetForProfile(profile.get());
LoginUIServiceMock* login_ui_service = static_cast<LoginUIServiceMock*>(
LoginUIServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile.get(), BuildMockLoginUIService));
SyncGlobalError error(&service, signin);
browser_sync::SyncBackendHost::Status status;
EXPECT_CALL(service, QueryDetailedSyncStatus(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(service, IsPassphraseRequired())
.WillRepeatedly(Return(true));
EXPECT_CALL(service, IsPassphraseRequiredForDecryption())
.WillRepeatedly(Return(true));
VerifySyncGlobalErrorResult(
&service, login_ui_service, browser(), &error,
GoogleServiceAuthError::NONE, true, true);
}
// Test that SyncGlobalError shows an error for conditions that can be resolved
// by the user and suppresses errors for conditions that cannot be resolved by
// the user.
TEST_F(SyncGlobalErrorTest, AuthStateGlobalError) {
scoped_ptr<Profile> profile(
ProfileSyncServiceMock::MakeSignedInTestingProfile());
NiceMock<ProfileSyncServiceMock> service(profile.get());
SigninManager* signin = SigninManagerFactory::GetForProfile(profile.get());
LoginUIServiceMock* login_ui_service = static_cast<LoginUIServiceMock*>(
LoginUIServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile.get(), BuildMockLoginUIService));
SyncGlobalError error(&service, signin);
browser_sync::SyncBackendHost::Status status;
EXPECT_CALL(service, QueryDetailedSyncStatus(_))
.WillRepeatedly(Return(false));
struct {
GoogleServiceAuthError::State error_state;
bool is_error;
} table[] = {
{ GoogleServiceAuthError::NONE, false },
{ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, true },
{ GoogleServiceAuthError::USER_NOT_SIGNED_UP, true },
{ GoogleServiceAuthError::CONNECTION_FAILED, false },
{ GoogleServiceAuthError::CAPTCHA_REQUIRED, true },
{ GoogleServiceAuthError::ACCOUNT_DELETED, true },
{ GoogleServiceAuthError::ACCOUNT_DISABLED, true },
{ GoogleServiceAuthError::SERVICE_UNAVAILABLE, true },
{ GoogleServiceAuthError::TWO_FACTOR, true },
{ GoogleServiceAuthError::REQUEST_CANCELED, true },
{ GoogleServiceAuthError::HOSTED_NOT_ALLOWED, true },
};
for (size_t i = 0; i < sizeof(table)/sizeof(*table); ++i) {
VerifySyncGlobalErrorResult(&service, login_ui_service, browser(), &error,
table[i].error_state, true, table[i].is_error);
VerifySyncGlobalErrorResult(&service, login_ui_service, browser(), &error,
table[i].error_state, false, false);
}
}