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.
5 #include "chrome/browser/services/gcm/gcm_account_tracker.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "components/gcm_driver/fake_gcm_driver.h"
12 #include "google_apis/gaia/fake_identity_provider.h"
13 #include "google_apis/gaia/fake_oauth2_token_service.h"
14 #include "google_apis/gaia/google_service_auth_error.h"
15 #include "net/http/http_status_code.h"
16 #include "net/url_request/test_url_fetcher_factory.h"
17 #include "net/url_request/url_request_test_util.h"
18 #include "testing/gtest/include/gtest/gtest.h"
24 const char kAccountId1
[] = "account_1";
25 const char kAccountId2
[] = "account_2";
27 std::string
AccountKeyToObfuscatedId(const std::string email
) {
28 return "obfid-" + email
;
31 std::string
GetValidTokenInfoResponse(const std::string account_key
) {
32 return std::string("{ \"id\": \"") + AccountKeyToObfuscatedId(account_key
) +
36 std::string
MakeAccessToken(const std::string
& account_key
) {
37 return "access_token-" + account_key
;
40 GCMClient::AccountTokenInfo
MakeAccountToken(const std::string
& account_key
) {
41 GCMClient::AccountTokenInfo token_info
;
42 token_info
.account_id
= account_key
;
43 token_info
.email
= account_key
;
44 token_info
.access_token
= MakeAccessToken(account_key
);
48 void VerifyAccountTokens(
49 const std::vector
<GCMClient::AccountTokenInfo
>& expected_tokens
,
50 const std::vector
<GCMClient::AccountTokenInfo
>& actual_tokens
) {
51 EXPECT_EQ(expected_tokens
.size(), actual_tokens
.size());
52 for (std::vector
<GCMClient::AccountTokenInfo
>::const_iterator
53 expected_iter
= expected_tokens
.begin(),
54 actual_iter
= actual_tokens
.begin();
55 expected_iter
!= expected_tokens
.end() &&
56 actual_iter
!= actual_tokens
.end();
57 ++expected_iter
, ++actual_iter
) {
58 EXPECT_EQ(expected_iter
->account_id
, actual_iter
->account_id
);
59 EXPECT_EQ(expected_iter
->email
, actual_iter
->email
);
60 EXPECT_EQ(expected_iter
->access_token
, actual_iter
->access_token
);
64 // This version of FakeGCMDriver is customized around handling accounts and
65 // connection events for testing GCMAccountTracker.
66 class CustomFakeGCMDriver
: public FakeGCMDriver
{
68 CustomFakeGCMDriver();
69 ~CustomFakeGCMDriver() override
;
71 // GCMDriver overrides:
72 void SetAccountTokens(
73 const std::vector
<GCMClient::AccountTokenInfo
>& account_tokens
) override
;
74 void AddConnectionObserver(GCMConnectionObserver
* observer
) override
;
75 void RemoveConnectionObserver(GCMConnectionObserver
* observer
) override
;
76 bool IsConnected() const override
{ return connected_
; }
77 base::Time
GetLastTokenFetchTime() override
;
78 void SetLastTokenFetchTime(const base::Time
& time
) override
;
80 // Test results and helpers.
81 void SetConnected(bool connected
);
83 bool update_accounts_called() const { return update_accounts_called_
; }
84 const std::vector
<GCMClient::AccountTokenInfo
>& accounts() const {
87 const GCMConnectionObserver
* last_connection_observer() const {
88 return last_connection_observer_
;
90 const GCMConnectionObserver
* last_removed_connection_observer() const {
91 return removed_connection_observer_
;
96 std::vector
<GCMClient::AccountTokenInfo
> accounts_
;
97 bool update_accounts_called_
;
98 GCMConnectionObserver
* last_connection_observer_
;
99 GCMConnectionObserver
* removed_connection_observer_
;
100 net::IPEndPoint ip_endpoint_
;
101 base::Time last_token_fetch_time_
;
103 DISALLOW_COPY_AND_ASSIGN(CustomFakeGCMDriver
);
106 CustomFakeGCMDriver::CustomFakeGCMDriver()
108 update_accounts_called_(false),
109 last_connection_observer_(NULL
),
110 removed_connection_observer_(NULL
) {
113 CustomFakeGCMDriver::~CustomFakeGCMDriver() {
116 void CustomFakeGCMDriver::SetAccountTokens(
117 const std::vector
<GCMClient::AccountTokenInfo
>& accounts
) {
118 update_accounts_called_
= true;
119 accounts_
= accounts
;
122 void CustomFakeGCMDriver::AddConnectionObserver(
123 GCMConnectionObserver
* observer
) {
124 last_connection_observer_
= observer
;
127 void CustomFakeGCMDriver::RemoveConnectionObserver(
128 GCMConnectionObserver
* observer
) {
129 removed_connection_observer_
= observer
;
132 void CustomFakeGCMDriver::SetConnected(bool connected
) {
133 connected_
= connected
;
134 if (connected
&& last_connection_observer_
)
135 last_connection_observer_
->OnConnected(ip_endpoint_
);
138 void CustomFakeGCMDriver::ResetResults() {
140 update_accounts_called_
= false;
141 last_connection_observer_
= NULL
;
142 removed_connection_observer_
= NULL
;
146 base::Time
CustomFakeGCMDriver::GetLastTokenFetchTime() {
147 return last_token_fetch_time_
;
150 void CustomFakeGCMDriver::SetLastTokenFetchTime(const base::Time
& time
) {
151 last_token_fetch_time_
= time
;
156 class GCMAccountTrackerTest
: public testing::Test
{
158 GCMAccountTrackerTest();
159 ~GCMAccountTrackerTest() override
;
161 // Helpers to pass fake events to the tracker. Tests should have either a pair
162 // of Start/FinishAccountSignIn or SignInAccount per account. Don't mix.
163 // Call to SignOutAccount is not mandatory.
164 void StartAccountSignIn(const std::string
& account_key
);
165 void FinishAccountSignIn(const std::string
& account_key
);
166 void SignInAccount(const std::string
& account_key
);
167 void SignOutAccount(const std::string
& account_key
);
169 // Helpers for dealing with OAuth2 access token requests.
170 void IssueAccessToken(const std::string
& account_key
);
171 void IssueExpiredAccessToken(const std::string
& account_key
);
172 void IssueError(const std::string
& account_key
);
174 // Accessors to account tracker and gcm driver.
175 GCMAccountTracker
* tracker() { return tracker_
.get(); }
176 CustomFakeGCMDriver
* driver() { return &driver_
; }
178 // Accessors to private methods of account tracker.
179 bool IsFetchingRequired() const;
180 bool IsTokenReportingRequired() const;
181 base::TimeDelta
GetTimeToNextTokenReporting() const;
184 CustomFakeGCMDriver driver_
;
186 base::MessageLoop message_loop_
;
187 net::TestURLFetcherFactory test_fetcher_factory_
;
188 scoped_ptr
<FakeOAuth2TokenService
> fake_token_service_
;
189 scoped_ptr
<FakeIdentityProvider
> fake_identity_provider_
;
190 scoped_ptr
<GCMAccountTracker
> tracker_
;
193 GCMAccountTrackerTest::GCMAccountTrackerTest() {
194 fake_token_service_
.reset(new FakeOAuth2TokenService());
196 fake_identity_provider_
.reset(
197 new FakeIdentityProvider(fake_token_service_
.get()));
199 scoped_ptr
<gaia::AccountTracker
> gaia_account_tracker(
200 new gaia::AccountTracker(
201 fake_identity_provider_
.get(),
202 new net::TestURLRequestContextGetter(message_loop_
.task_runner())));
204 tracker_
.reset(new GCMAccountTracker(gaia_account_tracker
.Pass(), &driver_
));
207 GCMAccountTrackerTest::~GCMAccountTrackerTest() {
209 tracker_
->Shutdown();
212 void GCMAccountTrackerTest::StartAccountSignIn(const std::string
& account_key
) {
213 fake_identity_provider_
->LogIn(account_key
);
214 fake_token_service_
->AddAccount(account_key
);
217 void GCMAccountTrackerTest::FinishAccountSignIn(
218 const std::string
& account_key
) {
219 IssueAccessToken(account_key
);
221 net::TestURLFetcher
* fetcher
= test_fetcher_factory_
.GetFetcherByID(
222 gaia::GaiaOAuthClient::kUrlFetcherId
);
223 ASSERT_TRUE(fetcher
);
224 fetcher
->set_response_code(net::HTTP_OK
);
225 fetcher
->SetResponseString(GetValidTokenInfoResponse(account_key
));
226 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
229 void GCMAccountTrackerTest::SignInAccount(const std::string
& account_key
) {
230 StartAccountSignIn(account_key
);
231 FinishAccountSignIn(account_key
);
234 void GCMAccountTrackerTest::SignOutAccount(const std::string
& account_key
) {
235 fake_token_service_
->RemoveAccount(account_key
);
238 void GCMAccountTrackerTest::IssueAccessToken(const std::string
& account_key
) {
239 fake_token_service_
->IssueAllTokensForAccount(
240 account_key
, MakeAccessToken(account_key
), base::Time::Max());
243 void GCMAccountTrackerTest::IssueExpiredAccessToken(
244 const std::string
& account_key
) {
245 fake_token_service_
->IssueAllTokensForAccount(
246 account_key
, MakeAccessToken(account_key
), base::Time::Now());
249 void GCMAccountTrackerTest::IssueError(const std::string
& account_key
) {
250 fake_token_service_
->IssueErrorForAllPendingRequestsForAccount(
252 GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE
));
255 bool GCMAccountTrackerTest::IsFetchingRequired() const {
256 return tracker_
->IsTokenFetchingRequired();
259 bool GCMAccountTrackerTest::IsTokenReportingRequired() const {
260 return tracker_
->IsTokenReportingRequired();
263 base::TimeDelta
GCMAccountTrackerTest::GetTimeToNextTokenReporting() const {
264 return tracker_
->GetTimeToNextTokenReporting();
267 TEST_F(GCMAccountTrackerTest
, NoAccounts
) {
268 EXPECT_FALSE(driver()->update_accounts_called());
270 // Callback should not be called if there where no accounts provided.
271 EXPECT_FALSE(driver()->update_accounts_called());
272 EXPECT_TRUE(driver()->accounts().empty());
275 // Verifies that callback is called after a token is issued for a single account
276 // with a specific scope. In this scenario, the underlying account tracker is
277 // still working when the CompleteCollectingTokens is called for the first time.
278 TEST_F(GCMAccountTrackerTest
, SingleAccount
) {
279 StartAccountSignIn(kAccountId1
);
282 // We don't have any accounts to report, but given the inner account tracker
283 // is still working we don't make a call with empty accounts list.
284 EXPECT_FALSE(driver()->update_accounts_called());
286 // This concludes the work of inner account tracker.
287 FinishAccountSignIn(kAccountId1
);
288 IssueAccessToken(kAccountId1
);
290 EXPECT_TRUE(driver()->update_accounts_called());
292 std::vector
<GCMClient::AccountTokenInfo
> expected_accounts
;
293 expected_accounts
.push_back(MakeAccountToken(kAccountId1
));
294 VerifyAccountTokens(expected_accounts
, driver()->accounts());
297 TEST_F(GCMAccountTrackerTest
, MultipleAccounts
) {
298 StartAccountSignIn(kAccountId1
);
299 StartAccountSignIn(kAccountId2
);
302 EXPECT_FALSE(driver()->update_accounts_called());
304 FinishAccountSignIn(kAccountId1
);
305 IssueAccessToken(kAccountId1
);
306 EXPECT_FALSE(driver()->update_accounts_called());
308 FinishAccountSignIn(kAccountId2
);
309 IssueAccessToken(kAccountId2
);
310 EXPECT_TRUE(driver()->update_accounts_called());
312 std::vector
<GCMClient::AccountTokenInfo
> expected_accounts
;
313 expected_accounts
.push_back(MakeAccountToken(kAccountId1
));
314 expected_accounts
.push_back(MakeAccountToken(kAccountId2
));
315 VerifyAccountTokens(expected_accounts
, driver()->accounts());
318 TEST_F(GCMAccountTrackerTest
, AccountAdded
) {
320 driver()->ResetResults();
322 SignInAccount(kAccountId1
);
323 EXPECT_FALSE(driver()->update_accounts_called());
325 IssueAccessToken(kAccountId1
);
326 EXPECT_TRUE(driver()->update_accounts_called());
328 std::vector
<GCMClient::AccountTokenInfo
> expected_accounts
;
329 expected_accounts
.push_back(MakeAccountToken(kAccountId1
));
330 VerifyAccountTokens(expected_accounts
, driver()->accounts());
333 TEST_F(GCMAccountTrackerTest
, AccountRemoved
) {
334 SignInAccount(kAccountId1
);
335 SignInAccount(kAccountId2
);
338 IssueAccessToken(kAccountId1
);
339 IssueAccessToken(kAccountId2
);
340 EXPECT_TRUE(driver()->update_accounts_called());
342 driver()->ResetResults();
343 EXPECT_FALSE(driver()->update_accounts_called());
345 SignOutAccount(kAccountId2
);
346 EXPECT_TRUE(driver()->update_accounts_called());
348 std::vector
<GCMClient::AccountTokenInfo
> expected_accounts
;
349 expected_accounts
.push_back(MakeAccountToken(kAccountId1
));
350 VerifyAccountTokens(expected_accounts
, driver()->accounts());
353 TEST_F(GCMAccountTrackerTest
, GetTokenFailed
) {
354 SignInAccount(kAccountId1
);
355 SignInAccount(kAccountId2
);
358 IssueAccessToken(kAccountId1
);
359 EXPECT_FALSE(driver()->update_accounts_called());
361 IssueError(kAccountId2
);
363 // Failed token is not retried any more. Account marked as removed.
364 EXPECT_EQ(0UL, tracker()->get_pending_token_request_count());
365 EXPECT_TRUE(driver()->update_accounts_called());
367 std::vector
<GCMClient::AccountTokenInfo
> expected_accounts
;
368 expected_accounts
.push_back(MakeAccountToken(kAccountId1
));
369 VerifyAccountTokens(expected_accounts
, driver()->accounts());
372 TEST_F(GCMAccountTrackerTest
, GetTokenFailedAccountRemoved
) {
373 SignInAccount(kAccountId1
);
374 SignInAccount(kAccountId2
);
377 IssueAccessToken(kAccountId1
);
379 driver()->ResetResults();
380 SignOutAccount(kAccountId2
);
381 IssueError(kAccountId2
);
383 EXPECT_TRUE(driver()->update_accounts_called());
385 std::vector
<GCMClient::AccountTokenInfo
> expected_accounts
;
386 expected_accounts
.push_back(MakeAccountToken(kAccountId1
));
387 VerifyAccountTokens(expected_accounts
, driver()->accounts());
390 TEST_F(GCMAccountTrackerTest
, AccountRemovedWhileRequestsPending
) {
391 SignInAccount(kAccountId1
);
392 SignInAccount(kAccountId2
);
395 IssueAccessToken(kAccountId1
);
396 EXPECT_FALSE(driver()->update_accounts_called());
398 SignOutAccount(kAccountId2
);
399 IssueAccessToken(kAccountId2
);
400 EXPECT_TRUE(driver()->update_accounts_called());
402 std::vector
<GCMClient::AccountTokenInfo
> expected_accounts
;
403 expected_accounts
.push_back(MakeAccountToken(kAccountId1
));
404 VerifyAccountTokens(expected_accounts
, driver()->accounts());
407 // Makes sure that tracker observes GCM connection when running.
408 TEST_F(GCMAccountTrackerTest
, TrackerObservesConnection
) {
409 EXPECT_EQ(NULL
, driver()->last_connection_observer());
411 EXPECT_EQ(tracker(), driver()->last_connection_observer());
412 tracker()->Shutdown();
413 EXPECT_EQ(tracker(), driver()->last_removed_connection_observer());
416 // Makes sure that token fetching happens only after connection is established.
417 TEST_F(GCMAccountTrackerTest
, PostponeTokenFetchingUntilConnected
) {
418 driver()->SetConnected(false);
419 StartAccountSignIn(kAccountId1
);
421 FinishAccountSignIn(kAccountId1
);
423 EXPECT_EQ(0UL, tracker()->get_pending_token_request_count());
424 driver()->SetConnected(true);
426 EXPECT_EQ(1UL, tracker()->get_pending_token_request_count());
429 TEST_F(GCMAccountTrackerTest
, InvalidateExpiredTokens
) {
430 StartAccountSignIn(kAccountId1
);
431 StartAccountSignIn(kAccountId2
);
433 FinishAccountSignIn(kAccountId1
);
434 FinishAccountSignIn(kAccountId2
);
436 EXPECT_EQ(2UL, tracker()->get_pending_token_request_count());
438 IssueExpiredAccessToken(kAccountId1
);
439 IssueAccessToken(kAccountId2
);
440 // Because the first token is expired, we expect the sanitize to kick in and
441 // clean it up before the SetAccessToken is called. This also means a new
442 // token request will be issued
443 EXPECT_FALSE(driver()->update_accounts_called());
444 EXPECT_EQ(1UL, tracker()->get_pending_token_request_count());
447 // Testing for whether there are still more tokens to be fetched. Typically the
448 // need for token fetching triggers immediate request, unless there is no
449 // connection, that is why connection is set on and off in this test.
450 TEST_F(GCMAccountTrackerTest
, IsTokenFetchingRequired
) {
452 driver()->SetConnected(false);
453 EXPECT_FALSE(IsFetchingRequired());
454 StartAccountSignIn(kAccountId1
);
455 FinishAccountSignIn(kAccountId1
);
456 EXPECT_TRUE(IsFetchingRequired());
458 driver()->SetConnected(true);
459 EXPECT_FALSE(IsFetchingRequired()); // Indicates that fetching has started.
460 IssueAccessToken(kAccountId1
);
461 EXPECT_FALSE(IsFetchingRequired());
463 driver()->SetConnected(false);
464 StartAccountSignIn(kAccountId2
);
465 FinishAccountSignIn(kAccountId2
);
466 EXPECT_TRUE(IsFetchingRequired());
468 IssueExpiredAccessToken(kAccountId2
);
469 // Make sure that if the token was expired it is still needed.
470 EXPECT_TRUE(IsFetchingRequired());
473 // Tests what is the expected time to the next token fetching.
474 TEST_F(GCMAccountTrackerTest
, GetTimeToNextTokenReporting
) {
476 // At this point the last token fetch time is never.
477 EXPECT_EQ(base::TimeDelta(), GetTimeToNextTokenReporting());
479 // Regular case. The tokens have been just reported.
480 driver()->SetLastTokenFetchTime(base::Time::Now());
481 EXPECT_TRUE(GetTimeToNextTokenReporting() <=
482 base::TimeDelta::FromSeconds(12 * 60 * 60));
484 // A case when gcm driver is not yet initialized.
485 driver()->SetLastTokenFetchTime(base::Time::Max());
486 EXPECT_EQ(base::TimeDelta::FromSeconds(12 * 60 * 60),
487 GetTimeToNextTokenReporting());
489 // A case when token reporting calculation is expected to result in more than
490 // 12 hours, in which case we expect exactly 12 hours.
491 driver()->SetLastTokenFetchTime(base::Time::Now() +
492 base::TimeDelta::FromDays(2));
493 EXPECT_EQ(base::TimeDelta::FromSeconds(12 * 60 * 60),
494 GetTimeToNextTokenReporting());
497 // Tests conditions when token reporting is required.
498 TEST_F(GCMAccountTrackerTest
, IsTokenReportingRequired
) {
500 // Required because it is overdue.
501 EXPECT_TRUE(IsTokenReportingRequired());
503 // Not required because it just happened.
504 driver()->SetLastTokenFetchTime(base::Time::Now());
505 EXPECT_FALSE(IsTokenReportingRequired());
507 SignInAccount(kAccountId1
);
508 IssueAccessToken(kAccountId1
);
509 driver()->ResetResults();
510 // Reporting was triggered, which means testing for required will give false,
511 // but we have the update call.
512 SignOutAccount(kAccountId1
);
513 EXPECT_TRUE(driver()->update_accounts_called());
514 EXPECT_FALSE(IsTokenReportingRequired());
517 // TODO(fgorski): Add test for adding account after removal >> make sure it does