signin_tracker_unittest.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 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/signin/signin_tracker.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/compiler_specific.h" 10#include "chrome/browser/profiles/profile.h" 11#include "chrome/browser/signin/fake_auth_status_provider.h" 12#include "chrome/browser/signin/signin_manager.h" 13#include "chrome/browser/signin/signin_manager_factory.h" 14#include "chrome/browser/signin/signin_manager_fake.h" 15#include "chrome/browser/signin/token_service.h" 16#include "chrome/browser/signin/token_service_factory.h" 17#include "chrome/browser/sync/profile_sync_service_factory.h" 18#include "chrome/browser/sync/profile_sync_service_mock.h" 19#include "chrome/common/chrome_notification_types.h" 20#include "content/public/browser/notification_service.h" 21#include "google_apis/gaia/gaia_constants.h" 22#include "google_apis/gaia/google_service_auth_error.h" 23 24#include "testing/gmock/include/gmock/gmock.h" 25#include "testing/gtest/include/gtest/gtest.h" 26 27using ::testing::_; 28using ::testing::AnyNumber; 29using ::testing::Mock; 30using ::testing::Return; 31using ::testing::ReturnRef; 32 33namespace { 34 35class MockTokenService : public TokenService { 36 public: 37 MockTokenService() { } 38 virtual ~MockTokenService() { } 39 40 MOCK_CONST_METHOD1(HasTokenForService, bool(const char*)); 41}; 42 43ProfileKeyedService* BuildMockTokenService(Profile* profile) { 44 return new MockTokenService; 45} 46 47class MockObserver : public SigninTracker::Observer { 48 public: 49 MockObserver() {} 50 ~MockObserver() {} 51 52 MOCK_METHOD0(GaiaCredentialsValid, void(void)); 53 MOCK_METHOD1(SigninFailed, void(const GoogleServiceAuthError&)); 54 MOCK_METHOD0(SigninSuccess, void(void)); 55}; 56 57} // namespace 58 59class SigninTrackerTest : public testing::Test { 60 public: 61 SigninTrackerTest() {} 62 virtual void SetUp() OVERRIDE { 63 profile_.reset(ProfileSyncServiceMock::MakeSignedInTestingProfile()); 64 mock_token_service_ = static_cast<MockTokenService*>( 65 TokenServiceFactory::GetInstance()->SetTestingFactoryAndUse( 66 profile_.get(), BuildMockTokenService)); 67 mock_pss_ = static_cast<ProfileSyncServiceMock*>( 68 ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse( 69 profile_.get(), 70 ProfileSyncServiceMock::BuildMockProfileSyncService)); 71 mock_pss_->Initialize(); 72 73 mock_signin_manager_ = static_cast<FakeSigninManager*>( 74 SigninManagerFactory::GetInstance()->SetTestingFactoryAndUse( 75 profile_.get(), FakeSigninManager::Build)); 76 77 // Make gmock not spam the output with information about these uninteresting 78 // calls. 79 EXPECT_CALL(*mock_pss_, AddObserver(_)).Times(AnyNumber()); 80 EXPECT_CALL(*mock_pss_, RemoveObserver(_)).Times(AnyNumber()); 81 tracker_.reset(new SigninTracker(profile_.get(), &observer_)); 82 } 83 virtual void TearDown() OVERRIDE { 84 tracker_.reset(); 85 profile_.reset(); 86 } 87 scoped_ptr<SigninTracker> tracker_; 88 scoped_ptr<TestingProfile> profile_; 89 ProfileSyncServiceMock* mock_pss_; 90 FakeSigninManager* mock_signin_manager_; 91 MockTokenService* mock_token_service_; 92 MockObserver observer_; 93}; 94 95TEST_F(SigninTrackerTest, GaiaSignInFailed) { 96 // SIGNIN_FAILED notification should result in a SigninFailed callback. 97 GoogleServiceAuthError error( 98 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 99 EXPECT_CALL(observer_, SigninFailed(error)); 100 content::NotificationService::current()->Notify( 101 chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED, 102 content::Source<Profile>(profile_.get()), 103 content::Details<const GoogleServiceAuthError>(&error)); 104} 105 106TEST_F(SigninTrackerTest, GaiaSignInSucceeded) { 107 // SIGNIN_SUCCEEDED notification should lead us to get a GaiCredentialsValid() 108 // callback. 109 EXPECT_CALL(observer_, GaiaCredentialsValid()); 110 EXPECT_CALL(*mock_pss_, IsSyncEnabledAndLoggedIn()) 111 .WillRepeatedly(Return(false)); 112 EXPECT_CALL(*mock_token_service_, HasTokenForService(_)) 113 .WillRepeatedly(Return(false)); 114 GoogleServiceSigninSuccessDetails details("username@gmail.com", "password"); 115 content::NotificationService::current()->Notify( 116 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 117 content::Source<Profile>(profile_.get()), 118 content::Details<const GoogleServiceSigninSuccessDetails>(&details)); 119} 120 121static void ExpectSignedInSyncService(ProfileSyncServiceMock* sync_service, 122 MockTokenService* token_service, 123 const GoogleServiceAuthError& error) { 124 if (token_service) { 125 EXPECT_CALL(*token_service, HasTokenForService(_)) 126 .WillRepeatedly(Return(true)); 127 } 128 EXPECT_CALL(*sync_service, IsSyncEnabledAndLoggedIn()).WillRepeatedly( 129 Return(true)); 130 EXPECT_CALL(*sync_service, IsSyncTokenAvailable()).WillRepeatedly( 131 Return(true)); 132 EXPECT_CALL(*sync_service, waiting_for_auth()).WillRepeatedly(Return(false)); 133 EXPECT_CALL(*sync_service, GetAuthError()).WillRepeatedly(ReturnRef(error)); 134 EXPECT_CALL(*sync_service, HasUnrecoverableError()).WillRepeatedly( 135 Return(false)); 136 EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(true)); 137 138} 139 140TEST_F(SigninTrackerTest, GaiaSigninWhenServicesAlreadyRunning) { 141 // SIGNIN_SUCCEEDED notification should result in a SigninSuccess() callback 142 // if we're already signed in. 143 EXPECT_CALL(observer_, GaiaCredentialsValid()); 144 EXPECT_CALL(observer_, SigninSuccess()); 145 GoogleServiceAuthError error(GoogleServiceAuthError::NONE); 146 ExpectSignedInSyncService(mock_pss_, mock_token_service_, error); 147 mock_signin_manager_->StartSignInWithCredentials("0", "username@gmail.com", 148 "password"); 149 GoogleServiceSigninSuccessDetails details("username@gmail.com", "password"); 150 content::NotificationService::current()->Notify( 151 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 152 content::Source<Profile>(profile_.get()), 153 content::Details<const GoogleServiceSigninSuccessDetails>(&details)); 154} 155 156TEST_F(SigninTrackerTest, NoGaiaSigninWhenOAuthTokensNotAvailable) { 157 // SIGNIN_SUCCESSFUL notification should not result in a SigninSuccess() 158 // callback if our oauth token hasn't been fetched. 159 EXPECT_CALL(observer_, GaiaCredentialsValid()); 160 GoogleServiceAuthError error(GoogleServiceAuthError::NONE); 161 ExpectSignedInSyncService(mock_pss_, NULL, error); 162 EXPECT_CALL(*mock_token_service_, 163 HasTokenForService(GaiaConstants::kSyncService)) 164 .WillRepeatedly(Return(true)); 165 EXPECT_CALL(*mock_token_service_, 166 HasTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken)) 167 .WillRepeatedly(Return(false)); 168 GoogleServiceSigninSuccessDetails details("username@gmail.com", "password"); 169 content::NotificationService::current()->Notify( 170 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 171 content::Source<Profile>(profile_.get()), 172 content::Details<const GoogleServiceSigninSuccessDetails>(&details)); 173} 174 175TEST_F(SigninTrackerTest, GaiaSigninAfterOAuthTokenBecomesAvailable) { 176 // SIGNIN_SUCCESSFUL notification should not result in a SigninSuccess() 177 // callback until after our oauth token hasn't been fetched. 178 EXPECT_CALL(observer_, GaiaCredentialsValid()); 179 GoogleServiceAuthError none(GoogleServiceAuthError::NONE); 180 ExpectSignedInSyncService(mock_pss_, NULL, none); 181 EXPECT_CALL(*mock_token_service_, 182 HasTokenForService(GaiaConstants::kSyncService)) 183 .WillRepeatedly(Return(true)); 184 EXPECT_CALL(*mock_token_service_, 185 HasTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken)) 186 .WillRepeatedly(Return(false)); 187 mock_signin_manager_->StartSignInWithCredentials("0", "username@gmail.com", 188 "password"); 189 GoogleServiceSigninSuccessDetails details("username@gmail.com", "password"); 190 content::NotificationService::current()->Notify( 191 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 192 content::Source<Profile>(profile_.get()), 193 content::Details<const GoogleServiceSigninSuccessDetails>(&details)); 194 Mock::VerifyAndClearExpectations(mock_pss_); 195 Mock::VerifyAndClearExpectations(mock_token_service_); 196 EXPECT_CALL(observer_, SigninSuccess()); 197 ExpectSignedInSyncService(mock_pss_, mock_token_service_, none); 198 TokenService::TokenAvailableDetails available( 199 GaiaConstants::kGaiaOAuth2LoginRefreshToken, "foo_token"); 200 content::NotificationService::current()->Notify( 201 chrome::NOTIFICATION_TOKEN_AVAILABLE, 202 content::Source<TokenService>(mock_token_service_), 203 content::Details<const TokenService::TokenAvailableDetails>(&available)); 204} 205 206TEST_F(SigninTrackerTest, SigninFailedWhenTokenFetchFails) { 207 // TOKEN_REQUEST_FAILED notification should result in SigninFailed() callback. 208 EXPECT_CALL(observer_, GaiaCredentialsValid()); 209 GoogleServiceAuthError none(GoogleServiceAuthError::NONE); 210 ExpectSignedInSyncService(mock_pss_, NULL, none); 211 EXPECT_CALL(*mock_token_service_, 212 HasTokenForService(GaiaConstants::kSyncService)) 213 .WillRepeatedly(Return(true)); 214 EXPECT_CALL(*mock_token_service_, 215 HasTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken)) 216 .WillRepeatedly(Return(false)); 217 GoogleServiceSigninSuccessDetails details("username@gmail.com", "password"); 218 content::NotificationService::current()->Notify( 219 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 220 content::Source<Profile>(profile_.get()), 221 content::Details<const GoogleServiceSigninSuccessDetails>(&details)); 222 223 GoogleServiceAuthError error( 224 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 225 EXPECT_CALL(observer_, SigninFailed(error)); 226 TokenService::TokenRequestFailedDetails failed( 227 GaiaConstants::kGaiaOAuth2LoginRefreshToken, error); 228 content::NotificationService::current()->Notify( 229 chrome::NOTIFICATION_TOKEN_REQUEST_FAILED, 230 content::Source<TokenService>(mock_token_service_), 231 content::Details<const TokenService::TokenRequestFailedDetails>(&failed)); 232} 233 234TEST_F(SigninTrackerTest, NoGaiaSigninWhenServicesNotRunning) { 235 // SIGNIN_SUCCEEDED notification should not result in a SigninSuccess() 236 // callback if we're not already signed in. 237 EXPECT_CALL(observer_, GaiaCredentialsValid()); 238 EXPECT_CALL(*mock_token_service_, HasTokenForService(_)) 239 .WillRepeatedly(Return(true)); 240 EXPECT_CALL(*mock_pss_, IsSyncEnabledAndLoggedIn()).WillRepeatedly( 241 Return(false)); 242 EXPECT_CALL(*mock_pss_, IsSyncTokenAvailable()).WillRepeatedly( 243 Return(false)); 244 GoogleServiceSigninSuccessDetails details("username@gmail.com", "password"); 245 content::NotificationService::current()->Notify( 246 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 247 content::Source<Profile>(profile_.get()), 248 content::Details<const GoogleServiceSigninSuccessDetails>(&details)); 249} 250 251TEST_F(SigninTrackerTest, GaiaSigninAfterSyncStarts) { 252 // Make sure that we don't get a SigninSuccess() callback until after the 253 // sync service reports that it's signed in. 254 EXPECT_CALL(observer_, GaiaCredentialsValid()); 255 EXPECT_CALL(*mock_pss_, IsSyncEnabledAndLoggedIn()).WillOnce( 256 Return(false)); 257 EXPECT_CALL(*mock_token_service_, HasTokenForService(_)) 258 .WillRepeatedly(Return(true)); 259 mock_signin_manager_->StartSignInWithCredentials("0", "username@gmail.com", 260 "password"); 261 GoogleServiceSigninSuccessDetails details("username@gmail.com", "password"); 262 content::NotificationService::current()->Notify( 263 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 264 content::Source<Profile>(profile_.get()), 265 content::Details<const GoogleServiceSigninSuccessDetails>(&details)); 266 Mock::VerifyAndClearExpectations(mock_pss_); 267 // Mimic the sync engine getting credentials. 268 EXPECT_CALL(observer_, SigninSuccess()); 269 GoogleServiceAuthError error(GoogleServiceAuthError::NONE); 270 ExpectSignedInSyncService(mock_pss_, mock_token_service_, error); 271 tracker_->OnStateChanged(); 272} 273 274TEST_F(SigninTrackerTest, SyncSigninError) { 275 // Make sure that we get a SigninFailed() callback if sync gets an error after 276 // initializaiton. 277 EXPECT_CALL(observer_, GaiaCredentialsValid()); 278 EXPECT_CALL(*mock_pss_, IsSyncEnabledAndLoggedIn()).WillRepeatedly( 279 Return(false)); 280 EXPECT_CALL(*mock_pss_, IsSyncTokenAvailable()).WillRepeatedly( 281 Return(false)); 282 EXPECT_CALL(*mock_token_service_, HasTokenForService(_)) 283 .WillRepeatedly(Return(true)); 284 mock_signin_manager_->StartSignInWithCredentials("0", "username@gmail.com", 285 "password"); 286 GoogleServiceSigninSuccessDetails details("username@gmail.com", "password"); 287 content::NotificationService::current()->Notify( 288 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 289 content::Source<Profile>(profile_.get()), 290 content::Details<const GoogleServiceSigninSuccessDetails>(&details)); 291 292 // Still waiting for auth, so sync state changes should be ignored. 293 EXPECT_CALL(*mock_pss_, waiting_for_auth()).WillOnce(Return(true)); 294 tracker_->OnStateChanged(); 295 // Now mimic an auth error - this should cause us to fail (not waiting for 296 // auth, but still have no credentials). 297 GoogleServiceAuthError error( 298 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 299 FakeAuthStatusProvider provider(mock_signin_manager_->signin_global_error()); 300 provider.SetAuthError(error); 301 EXPECT_CALL(*mock_pss_, waiting_for_auth()).WillRepeatedly(Return(false)); 302 EXPECT_CALL(observer_, SigninFailed(error)); 303 tracker_->OnStateChanged(); 304} 305 306// Test cases for initial state = SERVICES_INITIALIZING. 307TEST_F(SigninTrackerTest, SigninSuccess) { 308 // Reset the |tracker_| and restart with initial state parameter for its 309 // constructor later. 310 tracker_.reset(); 311 GoogleServiceAuthError error(GoogleServiceAuthError::NONE); 312 ExpectSignedInSyncService(mock_pss_, mock_token_service_, error); 313 mock_signin_manager_->StartSignInWithCredentials("0", "username@gmail.com", 314 "password"); 315 // Finally SigninSuccess() is expected to be called when everything is ready. 316 EXPECT_CALL(observer_, SigninSuccess()); 317 tracker_.reset(new SigninTracker(profile_.get(), &observer_, 318 SigninTracker::SERVICES_INITIALIZING)); 319} 320 321TEST_F(SigninTrackerTest, SigninFailedSyncTokenUnavailable) { 322 tracker_.reset(); 323 EXPECT_CALL(*mock_token_service_, HasTokenForService(_)) 324 .WillRepeatedly(Return(true)); 325 mock_signin_manager_->StartSignInWithCredentials("0", "username@gmail.com", 326 "password"); 327 GoogleServiceAuthError error(GoogleServiceAuthError::NONE); 328 EXPECT_CALL(*mock_pss_, IsSyncEnabledAndLoggedIn()).WillRepeatedly( 329 Return(true)); 330 // Inject Token unavailable error. 331 EXPECT_CALL(*mock_pss_, IsSyncTokenAvailable()).WillRepeatedly( 332 Return(false)); 333 EXPECT_CALL(*mock_pss_, waiting_for_auth()).WillRepeatedly(Return(false)); 334 EXPECT_CALL(*mock_pss_, GetAuthError()).WillRepeatedly(ReturnRef(error)); 335 EXPECT_CALL(*mock_pss_, HasUnrecoverableError()).WillRepeatedly( 336 Return(false)); 337 EXPECT_CALL(*mock_pss_, sync_initialized()).WillRepeatedly(Return(true)); 338 // Any error should result in SigninFailed() being called. 339 EXPECT_CALL(observer_, SigninFailed(error)); 340 tracker_.reset(new SigninTracker(profile_.get(), &observer_, 341 SigninTracker::SERVICES_INITIALIZING)); 342} 343 344TEST_F(SigninTrackerTest, SigninFailedGoogleServiceAuthError) { 345 tracker_.reset(); 346 EXPECT_CALL(*mock_token_service_, HasTokenForService(_)) 347 .WillRepeatedly(Return(true)); 348 mock_signin_manager_->StartSignInWithCredentials("0", "username@gmail.com", 349 "password"); 350 // Inject authentication error. 351 GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); 352 FakeAuthStatusProvider provider(mock_signin_manager_->signin_global_error()); 353 provider.SetAuthError(error); 354 EXPECT_CALL(*mock_pss_, IsSyncEnabledAndLoggedIn()).WillRepeatedly( 355 Return(true)); 356 EXPECT_CALL(*mock_pss_, IsSyncTokenAvailable()).WillRepeatedly( 357 Return(true)); 358 EXPECT_CALL(*mock_pss_, waiting_for_auth()).WillRepeatedly(Return(false)); 359 EXPECT_CALL(*mock_pss_, GetAuthError()).WillRepeatedly(ReturnRef(error)); 360 EXPECT_CALL(*mock_pss_, HasUnrecoverableError()).WillRepeatedly( 361 Return(false)); 362 EXPECT_CALL(*mock_pss_, sync_initialized()).WillRepeatedly(Return(true)); 363 // Any error should result in SigninFailed() being called. 364 EXPECT_CALL(observer_, SigninFailed(error)); 365 tracker_.reset(new SigninTracker(profile_.get(), &observer_, 366 SigninTracker::SERVICES_INITIALIZING)); 367} 368 369 370TEST_F(SigninTrackerTest, SigninFailedWhenInitializing) { 371 tracker_.reset(); 372 // SigninFailed() should be called. 373 GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED); 374 EXPECT_CALL(observer_, SigninFailed(error)); 375 tracker_.reset(new SigninTracker(profile_.get(), &observer_, 376 SigninTracker::SERVICES_INITIALIZING)); 377 tracker_->OnStateChanged(); 378} 379