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