Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / services / gcm / gcm_account_tracker_unittest.cc
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/services/gcm/gcm_account_tracker.h"
6
7 #include <map>
8 #include <string>
9
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"
19
20 namespace gcm {
21
22 namespace {
23
24 const char kAccountId1[] = "account_1";
25 const char kAccountId2[] = "account_2";
26
27 std::string AccountKeyToObfuscatedId(const std::string email) {
28   return "obfid-" + email;
29 }
30
31 std::string GetValidTokenInfoResponse(const std::string account_key) {
32   return std::string("{ \"id\": \"") + AccountKeyToObfuscatedId(account_key) +
33          "\" }";
34 }
35
36 std::string MakeAccessToken(const std::string& account_key) {
37   return "access_token-" + account_key;
38 }
39
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;
46 }
47
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);
61   }
62 }
63
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;
70
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;
79
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_;
86   }
87   const GCMConnectionObserver* last_connection_observer() const {
88     return last_connection_observer_;
89   }
90   const GCMConnectionObserver* last_removed_connection_observer() const {
91     return removed_connection_observer_;
92   }
93
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_;
102
103   DISALLOW_COPY_AND_ASSIGN(CustomFakeGCMDriver);
104 };
105
106 CustomFakeGCMDriver::CustomFakeGCMDriver()
107     : connected_(true),
108       update_accounts_called_(false),
109       last_connection_observer_(NULL),
110       removed_connection_observer_(NULL) {
111 }
112
113 CustomFakeGCMDriver::~CustomFakeGCMDriver() {
114 }
115
116 void CustomFakeGCMDriver::SetAccountTokens(
117     const std::vector<GCMClient::AccountTokenInfo>& accounts) {
118   update_accounts_called_ = true;
119   accounts_ = accounts;
120 }
121
122 void CustomFakeGCMDriver::AddConnectionObserver(
123     GCMConnectionObserver* observer) {
124   last_connection_observer_ = observer;
125 }
126
127 void CustomFakeGCMDriver::RemoveConnectionObserver(
128     GCMConnectionObserver* observer) {
129   removed_connection_observer_ = observer;
130 }
131
132 void CustomFakeGCMDriver::SetConnected(bool connected) {
133   connected_ = connected;
134   if (connected && last_connection_observer_)
135     last_connection_observer_->OnConnected(ip_endpoint_);
136 }
137
138 void CustomFakeGCMDriver::ResetResults() {
139   accounts_.clear();
140   update_accounts_called_ = false;
141   last_connection_observer_ = NULL;
142   removed_connection_observer_ = NULL;
143 }
144
145
146 base::Time CustomFakeGCMDriver::GetLastTokenFetchTime() {
147   return last_token_fetch_time_;
148 }
149
150 void CustomFakeGCMDriver::SetLastTokenFetchTime(const base::Time& time) {
151   last_token_fetch_time_ = time;
152 }
153
154 }  // namespace
155
156 class GCMAccountTrackerTest : public testing::Test {
157  public:
158   GCMAccountTrackerTest();
159   ~GCMAccountTrackerTest() override;
160
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);
168
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);
173
174   // Accessors to account tracker and gcm driver.
175   GCMAccountTracker* tracker() { return tracker_.get(); }
176   CustomFakeGCMDriver* driver() { return &driver_; }
177
178   // Accessors to private methods of account tracker.
179   bool IsFetchingRequired() const;
180   bool IsTokenReportingRequired() const;
181   base::TimeDelta GetTimeToNextTokenReporting() const;
182
183  private:
184   CustomFakeGCMDriver driver_;
185
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_;
191 };
192
193 GCMAccountTrackerTest::GCMAccountTrackerTest() {
194   fake_token_service_.reset(new FakeOAuth2TokenService());
195
196   fake_identity_provider_.reset(
197       new FakeIdentityProvider(fake_token_service_.get()));
198
199   scoped_ptr<gaia::AccountTracker> gaia_account_tracker(
200       new gaia::AccountTracker(fake_identity_provider_.get(),
201                                new net::TestURLRequestContextGetter(
202                                    message_loop_.message_loop_proxy())));
203
204   tracker_.reset(new GCMAccountTracker(gaia_account_tracker.Pass(), &driver_));
205 }
206
207 GCMAccountTrackerTest::~GCMAccountTrackerTest() {
208   if (tracker_)
209     tracker_->Shutdown();
210 }
211
212 void GCMAccountTrackerTest::StartAccountSignIn(const std::string& account_key) {
213   fake_identity_provider_->LogIn(account_key);
214   fake_token_service_->AddAccount(account_key);
215 }
216
217 void GCMAccountTrackerTest::FinishAccountSignIn(
218     const std::string& account_key) {
219   IssueAccessToken(account_key);
220
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);
227 }
228
229 void GCMAccountTrackerTest::SignInAccount(const std::string& account_key) {
230   StartAccountSignIn(account_key);
231   FinishAccountSignIn(account_key);
232 }
233
234 void GCMAccountTrackerTest::SignOutAccount(const std::string& account_key) {
235   fake_token_service_->RemoveAccount(account_key);
236 }
237
238 void GCMAccountTrackerTest::IssueAccessToken(const std::string& account_key) {
239   fake_token_service_->IssueAllTokensForAccount(
240       account_key, MakeAccessToken(account_key), base::Time::Max());
241 }
242
243 void GCMAccountTrackerTest::IssueExpiredAccessToken(
244     const std::string& account_key) {
245   fake_token_service_->IssueAllTokensForAccount(
246       account_key, MakeAccessToken(account_key), base::Time::Now());
247 }
248
249 void GCMAccountTrackerTest::IssueError(const std::string& account_key) {
250   fake_token_service_->IssueErrorForAllPendingRequestsForAccount(
251       account_key,
252       GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
253 }
254
255 bool GCMAccountTrackerTest::IsFetchingRequired() const {
256   return tracker_->IsTokenFetchingRequired();
257 }
258
259 bool GCMAccountTrackerTest::IsTokenReportingRequired() const {
260   return tracker_->IsTokenReportingRequired();
261 }
262
263 base::TimeDelta GCMAccountTrackerTest::GetTimeToNextTokenReporting() const {
264   return tracker_->GetTimeToNextTokenReporting();
265 }
266
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());
273 }
274
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);
280
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());
285
286   // This concludes the work of inner account tracker.
287   FinishAccountSignIn(kAccountId1);
288   IssueAccessToken(kAccountId1);
289
290   EXPECT_TRUE(driver()->update_accounts_called());
291
292   std::vector<GCMClient::AccountTokenInfo> expected_accounts;
293   expected_accounts.push_back(MakeAccountToken(kAccountId1));
294   VerifyAccountTokens(expected_accounts, driver()->accounts());
295 }
296
297 TEST_F(GCMAccountTrackerTest, MultipleAccounts) {
298   StartAccountSignIn(kAccountId1);
299   StartAccountSignIn(kAccountId2);
300
301   tracker()->Start();
302   EXPECT_FALSE(driver()->update_accounts_called());
303
304   FinishAccountSignIn(kAccountId1);
305   IssueAccessToken(kAccountId1);
306   EXPECT_FALSE(driver()->update_accounts_called());
307
308   FinishAccountSignIn(kAccountId2);
309   IssueAccessToken(kAccountId2);
310   EXPECT_TRUE(driver()->update_accounts_called());
311
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());
316 }
317
318 TEST_F(GCMAccountTrackerTest, AccountAdded) {
319   tracker()->Start();
320   driver()->ResetResults();
321
322   SignInAccount(kAccountId1);
323   EXPECT_FALSE(driver()->update_accounts_called());
324
325   IssueAccessToken(kAccountId1);
326   EXPECT_TRUE(driver()->update_accounts_called());
327
328   std::vector<GCMClient::AccountTokenInfo> expected_accounts;
329   expected_accounts.push_back(MakeAccountToken(kAccountId1));
330   VerifyAccountTokens(expected_accounts, driver()->accounts());
331 }
332
333 TEST_F(GCMAccountTrackerTest, AccountRemoved) {
334   SignInAccount(kAccountId1);
335   SignInAccount(kAccountId2);
336
337   tracker()->Start();
338   IssueAccessToken(kAccountId1);
339   IssueAccessToken(kAccountId2);
340   EXPECT_TRUE(driver()->update_accounts_called());
341
342   driver()->ResetResults();
343   EXPECT_FALSE(driver()->update_accounts_called());
344
345   SignOutAccount(kAccountId2);
346   EXPECT_TRUE(driver()->update_accounts_called());
347
348   std::vector<GCMClient::AccountTokenInfo> expected_accounts;
349   expected_accounts.push_back(MakeAccountToken(kAccountId1));
350   VerifyAccountTokens(expected_accounts, driver()->accounts());
351 }
352
353 TEST_F(GCMAccountTrackerTest, GetTokenFailed) {
354   SignInAccount(kAccountId1);
355   SignInAccount(kAccountId2);
356
357   tracker()->Start();
358   IssueAccessToken(kAccountId1);
359   EXPECT_FALSE(driver()->update_accounts_called());
360
361   IssueError(kAccountId2);
362   EXPECT_FALSE(driver()->update_accounts_called());
363
364   EXPECT_EQ(1UL, tracker()->get_pending_token_request_count());
365
366   IssueAccessToken(kAccountId2);
367   EXPECT_TRUE(driver()->update_accounts_called());
368
369   std::vector<GCMClient::AccountTokenInfo> expected_accounts;
370   expected_accounts.push_back(MakeAccountToken(kAccountId1));
371   expected_accounts.push_back(MakeAccountToken(kAccountId2));
372   VerifyAccountTokens(expected_accounts, driver()->accounts());
373 }
374
375 TEST_F(GCMAccountTrackerTest, GetTokenFailedAccountRemoved) {
376   SignInAccount(kAccountId1);
377   SignInAccount(kAccountId2);
378
379   tracker()->Start();
380   IssueAccessToken(kAccountId1);
381   IssueError(kAccountId2);
382
383   driver()->ResetResults();
384   SignOutAccount(kAccountId2);
385   IssueError(kAccountId2);
386
387   EXPECT_TRUE(driver()->update_accounts_called());
388
389   std::vector<GCMClient::AccountTokenInfo> expected_accounts;
390   expected_accounts.push_back(MakeAccountToken(kAccountId1));
391   VerifyAccountTokens(expected_accounts, driver()->accounts());
392 }
393
394 TEST_F(GCMAccountTrackerTest, AccountRemovedWhileRequestsPending) {
395   SignInAccount(kAccountId1);
396   SignInAccount(kAccountId2);
397
398   tracker()->Start();
399   IssueAccessToken(kAccountId1);
400   EXPECT_FALSE(driver()->update_accounts_called());
401
402   SignOutAccount(kAccountId2);
403   IssueAccessToken(kAccountId2);
404   EXPECT_TRUE(driver()->update_accounts_called());
405
406   std::vector<GCMClient::AccountTokenInfo> expected_accounts;
407   expected_accounts.push_back(MakeAccountToken(kAccountId1));
408   VerifyAccountTokens(expected_accounts, driver()->accounts());
409 }
410
411 // Makes sure that tracker observes GCM connection when running.
412 TEST_F(GCMAccountTrackerTest, TrackerObservesConnection) {
413   EXPECT_EQ(NULL, driver()->last_connection_observer());
414   tracker()->Start();
415   EXPECT_EQ(tracker(), driver()->last_connection_observer());
416   tracker()->Shutdown();
417   EXPECT_EQ(tracker(), driver()->last_removed_connection_observer());
418 }
419
420 // Makes sure that token fetching happens only after connection is established.
421 TEST_F(GCMAccountTrackerTest, PostponeTokenFetchingUntilConnected) {
422   driver()->SetConnected(false);
423   StartAccountSignIn(kAccountId1);
424   tracker()->Start();
425   FinishAccountSignIn(kAccountId1);
426
427   EXPECT_EQ(0UL, tracker()->get_pending_token_request_count());
428   driver()->SetConnected(true);
429
430   EXPECT_EQ(1UL, tracker()->get_pending_token_request_count());
431 }
432
433 TEST_F(GCMAccountTrackerTest, InvalidateExpiredTokens) {
434   StartAccountSignIn(kAccountId1);
435   StartAccountSignIn(kAccountId2);
436   tracker()->Start();
437   FinishAccountSignIn(kAccountId1);
438   FinishAccountSignIn(kAccountId2);
439
440   EXPECT_EQ(2UL, tracker()->get_pending_token_request_count());
441
442   IssueExpiredAccessToken(kAccountId1);
443   IssueAccessToken(kAccountId2);
444   // Because the first token is expired, we expect the sanitize to kick in and
445   // clean it up before the SetAccessToken is called. This also means a new
446   // token request will be issued
447   EXPECT_FALSE(driver()->update_accounts_called());
448   EXPECT_EQ(1UL, tracker()->get_pending_token_request_count());
449 }
450
451 // Testing for whether there are still more tokens to be fetched. Typically the
452 // need for token fetching triggers immediate request, unless there is no
453 // connection, that is why connection is set on and off in this test.
454 TEST_F(GCMAccountTrackerTest, IsTokenFetchingRequired) {
455   tracker()->Start();
456   driver()->SetConnected(false);
457   EXPECT_FALSE(IsFetchingRequired());
458   StartAccountSignIn(kAccountId1);
459   FinishAccountSignIn(kAccountId1);
460   EXPECT_TRUE(IsFetchingRequired());
461
462   driver()->SetConnected(true);
463   EXPECT_FALSE(IsFetchingRequired());  // Indicates that fetching has started.
464   IssueAccessToken(kAccountId1);
465   EXPECT_FALSE(IsFetchingRequired());
466
467   driver()->SetConnected(false);
468   StartAccountSignIn(kAccountId2);
469   FinishAccountSignIn(kAccountId2);
470   EXPECT_TRUE(IsFetchingRequired());
471
472   IssueExpiredAccessToken(kAccountId2);
473   // Make sure that if the token was expired it is still needed.
474   EXPECT_TRUE(IsFetchingRequired());
475 }
476
477 // Tests what is the expected time to the next token fetching.
478 TEST_F(GCMAccountTrackerTest, GetTimeToNextTokenReporting) {
479   tracker()->Start();
480   // At this point the last token fetch time is never.
481   EXPECT_EQ(base::TimeDelta(), GetTimeToNextTokenReporting());
482
483   driver()->SetLastTokenFetchTime(base::Time::Now());
484   EXPECT_TRUE(GetTimeToNextTokenReporting() <=
485                   base::TimeDelta::FromSeconds(12 * 60 * 60));
486 }
487
488 // Tests conditions when token reporting is required.
489 TEST_F(GCMAccountTrackerTest, IsTokenReportingRequired) {
490   tracker()->Start();
491   // Required because it is overdue.
492   EXPECT_TRUE(IsTokenReportingRequired());
493
494   // Not required because it just happened.
495   driver()->SetLastTokenFetchTime(base::Time::Now());
496   EXPECT_FALSE(IsTokenReportingRequired());
497
498   SignInAccount(kAccountId1);
499   IssueAccessToken(kAccountId1);
500   driver()->ResetResults();
501   // Reporting was triggered, which means testing for required will give false,
502   // but we have the update call.
503   SignOutAccount(kAccountId1);
504   EXPECT_TRUE(driver()->update_accounts_called());
505   EXPECT_FALSE(IsTokenReportingRequired());
506 }
507
508 // TODO(fgorski): Add test for adding account after removal >> make sure it does
509 // not mark removal.
510
511 }  // namespace gcm