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 32 using ::testing::NiceMock; 33 using ::testing::Return; 34 using ::testing::ReturnRef; 35 using ::testing::_; 36 37 namespace ash { 38 namespace test { 39 40 namespace { 41 42 static const char kTestAccountId[] = "testuser (at) test.com"; 43 44 // Notification ID corresponding to kProfileSyncNotificationId + kTestAccountId. 45 static const std::string kNotificationId = 46 "chrome://settings/sync/testuser@test.com"; 47 48 #if defined(OS_WIN) 49 class 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 65 class FakeLoginUIService: public LoginUIService { 66 public: 67 FakeLoginUIService() : LoginUIService(NULL) {} 68 virtual ~FakeLoginUIService() {} 69 }; 70 71 class 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 89 KeyedService* BuildMockLoginUIService( 90 content::BrowserContext* profile) { 91 return new FakeLoginUIService(); 92 } 93 94 class 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 195 TEST_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