1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/sync/sync_error_notifier_ash.h" 6 7#include "ash/test/ash_test_base.h" 8#include "base/memory/scoped_ptr.h" 9#include "chrome/browser/browser_process.h" 10#include "chrome/browser/notifications/notification.h" 11#include "chrome/browser/notifications/notification_ui_manager.h" 12#include "chrome/browser/sync/profile_sync_service_mock.h" 13#include "chrome/browser/sync/sync_error_controller.h" 14#include "chrome/browser/ui/browser.h" 15#include "chrome/browser/ui/webui/signin/login_ui_service.h" 16#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" 17#include "chrome/common/pref_names.h" 18#include "chrome/test/base/testing_browser_process.h" 19#include "chrome/test/base/testing_profile.h" 20#include "chrome/test/base/testing_profile_manager.h" 21#include "testing/gmock/include/gmock/gmock.h" 22#include "testing/gtest/include/gtest/gtest.h" 23#include "ui/message_center/notification.h" 24 25#if defined(OS_WIN) 26#include "chrome/browser/ui/ash/ash_util.h" 27#include "ui/aura/test/test_screen.h" 28#include "ui/gfx/screen.h" 29#include "ui/gfx/screen_type_delegate.h" 30#endif 31 32using ::testing::NiceMock; 33using ::testing::Return; 34using ::testing::ReturnRef; 35using ::testing::_; 36 37namespace ash { 38namespace test { 39 40namespace { 41 42static const char kTestAccountId[] = "testuser@test.com"; 43 44// Notification ID corresponding to kProfileSyncNotificationId + kTestAccountId. 45static const std::string kNotificationId = 46 "chrome://settings/sync/testuser@test.com"; 47 48#if defined(OS_WIN) 49class ScreenTypeDelegateDesktop : public gfx::ScreenTypeDelegate { 50 public: 51 ScreenTypeDelegateDesktop() {} 52 virtual ~ScreenTypeDelegateDesktop() {} 53 virtual gfx::ScreenType GetScreenTypeForNativeView( 54 gfx::NativeView view) OVERRIDE { 55 return chrome::IsNativeViewInAsh(view) ? 56 gfx::SCREEN_TYPE_ALTERNATE : 57 gfx::SCREEN_TYPE_NATIVE; 58 } 59 60 private: 61 DISALLOW_COPY_AND_ASSIGN(ScreenTypeDelegateDesktop); 62}; 63#endif 64 65class FakeLoginUIService: public LoginUIService { 66 public: 67 FakeLoginUIService() : LoginUIService(NULL) {} 68 virtual ~FakeLoginUIService() {} 69}; 70 71class FakeLoginUI : public LoginUIService::LoginUI { 72 public: 73 FakeLoginUI() : focus_ui_call_count_(0) {} 74 75 virtual ~FakeLoginUI() {} 76 77 int focus_ui_call_count() const { return focus_ui_call_count_; } 78 79 private: 80 // LoginUIService::LoginUI: 81 virtual void FocusUI() OVERRIDE { 82 ++focus_ui_call_count_; 83 } 84 virtual void CloseUI() OVERRIDE {} 85 86 int focus_ui_call_count_; 87}; 88 89KeyedService* BuildMockLoginUIService( 90 content::BrowserContext* profile) { 91 return new FakeLoginUIService(); 92} 93 94class SyncErrorNotifierTest : public AshTestBase { 95 public: 96 SyncErrorNotifierTest() {} 97 virtual ~SyncErrorNotifierTest() {} 98 99 virtual void SetUp() OVERRIDE { 100 profile_manager_.reset( 101 new TestingProfileManager(TestingBrowserProcess::GetGlobal())); 102 ASSERT_TRUE(profile_manager_->SetUp()); 103 104 profile_ = profile_manager_->CreateTestingProfile(kTestAccountId); 105 106 TestingBrowserProcess::GetGlobal(); 107 AshTestBase::SetUp(); 108 109 // Set up a desktop screen for Windows to hold native widgets, used when 110 // adding desktop widgets (i.e., message center notifications). 111#if defined(OS_WIN) 112 test_screen_.reset(aura::TestScreen::Create(gfx::Size())); 113 gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen_.get()); 114 gfx::Screen::SetScreenTypeDelegate(new ScreenTypeDelegateDesktop); 115#endif 116 117 service_.reset(new NiceMock<ProfileSyncServiceMock>(profile_)); 118 119 FakeLoginUIService* login_ui_service = static_cast<FakeLoginUIService*>( 120 LoginUIServiceFactory::GetInstance()->SetTestingFactoryAndUse( 121 profile_, BuildMockLoginUIService)); 122 login_ui_service->SetLoginUI(&login_ui_); 123 124 error_controller_.reset(new SyncErrorController(service_.get())); 125 error_notifier_.reset(new SyncErrorNotifier(error_controller_.get(), 126 profile_)); 127 128 notification_ui_manager_ = g_browser_process->notification_ui_manager(); 129 } 130 131 virtual void TearDown() OVERRIDE { 132 error_notifier_->Shutdown(); 133 service_.reset(); 134#if defined(OS_WIN) 135 test_screen_.reset(); 136#endif 137 profile_manager_.reset(); 138 139 AshTestBase::TearDown(); 140 } 141 142 protected: 143 // Utility function to test that SyncErrorNotifier behaves correctly for the 144 // given error condition. 145 void VerifySyncErrorNotifierResult(GoogleServiceAuthError::State error_state, 146 bool is_signed_in, 147 bool is_error) { 148 EXPECT_CALL(*service_, HasSyncSetupCompleted()) 149 .WillRepeatedly(Return(is_signed_in)); 150 151 GoogleServiceAuthError auth_error(error_state); 152 EXPECT_CALL(*service_, GetAuthError()).WillRepeatedly( 153 ReturnRef(auth_error)); 154 155 error_controller_->OnStateChanged(); 156 EXPECT_EQ(is_error, error_controller_->HasError()); 157 158 // If there is an error we should see a notification. 159 const Notification* notification = notification_ui_manager_-> 160 FindById(kNotificationId); 161 if (is_error) { 162 ASSERT_TRUE(notification); 163 ASSERT_FALSE(notification->title().empty()); 164 ASSERT_FALSE(notification->title().empty()); 165 ASSERT_EQ((size_t)1, notification->buttons().size()); 166 } else { 167 ASSERT_FALSE(notification); 168 } 169 } 170 171#if defined(OS_WIN) 172 scoped_ptr<gfx::Screen> test_screen_; 173#endif 174 scoped_ptr<TestingProfileManager> profile_manager_; 175 scoped_ptr<SyncErrorController> error_controller_; 176 scoped_ptr<SyncErrorNotifier> error_notifier_; 177 scoped_ptr<NiceMock<ProfileSyncServiceMock> > service_; 178 TestingProfile* profile_; 179 FakeLoginUI login_ui_; 180 NotificationUIManager* notification_ui_manager_; 181 182 DISALLOW_COPY_AND_ASSIGN(SyncErrorNotifierTest); 183}; 184 185} // namespace 186 187// Test that SyncErrorNotifier shows an notification if a passphrase is 188// required. 189// Disabled on Windows: http://crbug.com/373238 190#if defined(OS_WIN) 191#define MAYBE_PassphraseNotification DISABLED_PassphraseNotification 192#else 193#define MAYBE_PassphraseNotification PassphraseNotification 194#endif 195TEST_F(SyncErrorNotifierTest, MAYBE_PassphraseNotification) { 196 ASSERT_FALSE(notification_ui_manager_->FindById(kNotificationId)); 197 198 browser_sync::SyncBackendHost::Status status; 199 EXPECT_CALL(*service_, QueryDetailedSyncStatus(_)) 200 .WillRepeatedly(Return(false)); 201 202 EXPECT_CALL(*service_, IsPassphraseRequired()) 203 .WillRepeatedly(Return(true)); 204 EXPECT_CALL(*service_, IsPassphraseRequiredForDecryption()) 205 .WillRepeatedly(Return(true)); 206 { 207 SCOPED_TRACE("Expected a notification for passphrase error"); 208 VerifySyncErrorNotifierResult(GoogleServiceAuthError::NONE, 209 true /* signed in */, 210 true /* error */); 211 } 212 213 // Check that no notification is shown if there is no error. 214 EXPECT_CALL(*service_, IsPassphraseRequired()) 215 .WillRepeatedly(Return(false)); 216 EXPECT_CALL(*service_, IsPassphraseRequiredForDecryption()) 217 .WillRepeatedly(Return(false)); 218 { 219 SCOPED_TRACE("Not expecting notification since no error exists"); 220 VerifySyncErrorNotifierResult(GoogleServiceAuthError::NONE, 221 true /* signed in */, 222 false /* no error */); 223 } 224 225 // Check that no notification is shown if sync setup is not completed. 226 EXPECT_CALL(*service_, IsPassphraseRequired()) 227 .WillRepeatedly(Return(true)); 228 EXPECT_CALL(*service_, IsPassphraseRequiredForDecryption()) 229 .WillRepeatedly(Return(true)); 230 { 231 SCOPED_TRACE("Not expecting notification since sync setup is incomplete"); 232 VerifySyncErrorNotifierResult( 233 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, 234 false /* not signed in */, 235 false /* no error */); 236 } 237} 238 239} // namespace test 240} // namespace ash 241