Componentize VariationsService into //components/variations/service
[chromium-blink-merge.git] / components / gcm_driver / gcm_account_tracker_unittest.cc
blob38d15328929ae3d4925158f0097ea0d90bd7301b
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 "components/gcm_driver/gcm_account_tracker.h"
7 #include <map>
8 #include <string>
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"
20 namespace gcm {
22 namespace {
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) +
33 "\" }";
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);
45 return token_info;
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 {
67 public:
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);
82 void ResetResults();
83 bool update_accounts_called() const { return update_accounts_called_; }
84 const std::vector<GCMClient::AccountTokenInfo>& accounts() const {
85 return accounts_;
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_;
94 private:
95 bool connected_;
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()
107 : connected_(true),
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() {
139 accounts_.clear();
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;
154 } // namespace
156 class GCMAccountTrackerTest : public testing::Test {
157 public:
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;
183 private:
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() {
208 if (tracker_)
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(
251 account_key,
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());
269 tracker()->Start();
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);
281 tracker()->Start();
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);
301 tracker()->Start();
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) {
319 tracker()->Start();
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);
337 tracker()->Start();
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);
357 tracker()->Start();
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);
376 tracker()->Start();
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);
394 tracker()->Start();
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());
410 tracker()->Start();
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);
420 tracker()->Start();
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);
432 tracker()->Start();
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) {
451 tracker()->Start();
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) {
475 tracker()->Start();
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) {
499 tracker()->Start();
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
518 // not mark removal.
520 } // namespace gcm