Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: google_apis/gaia/account_tracker_unittest.cc

Issue 336253002: Add IdentityProvider-based AccountTracker to google_apis (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/api/identity/account_tracker.h" 5 #include "google_apis/gaia/account_tracker.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/message_loop/message_loop.h"
10 #include "base/strings/stringprintf.h" 11 #include "base/strings/stringprintf.h"
11 #include "chrome/browser/signin/fake_profile_oauth2_token_service.h" 12 #include "google_apis/gaia/fake_identity_provider.h"
12 #include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h" 13 #include "google_apis/gaia/fake_oauth2_token_service.h"
13 #include "chrome/browser/signin/fake_signin_manager.h"
14 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
15 #include "chrome/browser/signin/signin_manager_factory.h"
16 #include "chrome/test/base/testing_profile.h"
17 #include "components/signin/core/browser/signin_manager_base.h"
18 #include "content/public/test/test_browser_thread_bundle.h"
19 #include "google_apis/gaia/gaia_oauth_client.h" 14 #include "google_apis/gaia/gaia_oauth_client.h"
20 #include "net/http/http_status_code.h" 15 #include "net/http/http_status_code.h"
21 #include "net/url_request/test_url_fetcher_factory.h" 16 #include "net/url_request/test_url_fetcher_factory.h"
22 #include "net/url_request/url_fetcher_delegate.h" 17 #include "net/url_request/url_fetcher_delegate.h"
23 #include "net/url_request/url_request_test_util.h" 18 #include "net/url_request/url_request_test_util.h"
24 #include "testing/gtest/include/gtest/gtest.h" 19 #include "testing/gtest/include/gtest/gtest.h"
25 20
26 // TODO(courage): Account removal really only applies to the primary account,
27 // because that's the only account tracked by the SigninManager. Many of the
28 // tests here remove non-primary accounts. They still properly test the account
29 // state machine, but it may be confusing to readers. Update these tests to
30 // avoid causing confusion.
31
32 namespace { 21 namespace {
33 22
34 const char kPrimaryAccountKey[] = "primary_account@example.com"; 23 const char kPrimaryAccountKey[] = "primary_account@example.com";
35 24
36 enum TrackingEventType { 25 enum TrackingEventType {
37 ADDED, 26 ADDED,
38 REMOVED, 27 REMOVED,
39 SIGN_IN, 28 SIGN_IN,
40 SIGN_OUT 29 SIGN_OUT
41 }; 30 };
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
107 str += ",\n "; 96 str += ",\n ";
108 needs_comma = true; 97 needs_comma = true;
109 str += it->ToString(); 98 str += it->ToString();
110 } 99 }
111 str += "]"; 100 str += "]";
112 return str; 101 return str;
113 } 102 }
114 103
115 } // namespace 104 } // namespace
116 105
117 namespace extensions { 106 namespace gaia {
118 107
119 class AccountTrackerObserver : public AccountTracker::Observer { 108 class AccountTrackerObserver : public AccountTracker::Observer {
120 public: 109 public:
121 AccountTrackerObserver() {} 110 AccountTrackerObserver() {}
122 virtual ~AccountTrackerObserver() {} 111 virtual ~AccountTrackerObserver() {}
123 112
124 testing::AssertionResult CheckEvents(); 113 testing::AssertionResult CheckEvents();
125 testing::AssertionResult CheckEvents(const TrackingEvent& e1); 114 testing::AssertionResult CheckEvents(const TrackingEvent& e1);
126 testing::AssertionResult CheckEvents(const TrackingEvent& e1, 115 testing::AssertionResult CheckEvents(const TrackingEvent& e1,
127 const TrackingEvent& e2); 116 const TrackingEvent& e2);
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after
271 return result; 260 return result;
272 } 261 }
273 262
274 class IdentityAccountTrackerTest : public testing::Test { 263 class IdentityAccountTrackerTest : public testing::Test {
275 public: 264 public:
276 IdentityAccountTrackerTest() {} 265 IdentityAccountTrackerTest() {}
277 266
278 virtual ~IdentityAccountTrackerTest() {} 267 virtual ~IdentityAccountTrackerTest() {}
279 268
280 virtual void SetUp() OVERRIDE { 269 virtual void SetUp() OVERRIDE {
281 TestingProfile::Builder builder;
282 builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(),
283 BuildFakeProfileOAuth2TokenService);
284 builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
285 FakeSigninManagerBase::Build);
286 270
287 test_profile_ = builder.Build(); 271 fake_oauth2_token_service_.reset(new FakeOAuth2TokenService());
288 272
289 fake_oauth2_token_service_ = static_cast<FakeProfileOAuth2TokenService*>( 273 scoped_ptr<FakeIdentityProvider> fake_identity_provider(
290 ProfileOAuth2TokenServiceFactory::GetForProfile(test_profile_.get())); 274 new FakeIdentityProvider(fake_oauth2_token_service_.get()));
291 275
292 fake_signin_manager_ = static_cast<FakeSigninManagerForTesting*>( 276 account_tracker_.reset(
293 SigninManagerFactory::GetForProfile(test_profile_.get())); 277 new AccountTracker(fake_identity_provider.PassAs<IdentityProvider>(),
294 #if defined(OS_CHROMEOS) 278 new net::TestURLRequestContextGetter(
295 // We don't sign the primary user in and out on ChromeOS, so set the 279 message_loop_.message_loop_proxy())));
296 // username once in setup.
297 fake_signin_manager_->SetAuthenticatedUsername(kPrimaryAccountKey);
298 #endif
299
300 account_tracker_.reset(new AccountTracker(test_profile_.get()));
301 account_tracker_->AddObserver(&observer_); 280 account_tracker_->AddObserver(&observer_);
302
303 // Start off signed into the primary account, because most tests need the
304 // profile to be signed in. Remove the initial sign-in events that the
305 // tests don't care about.
306 NotifyTokenAvailable(kPrimaryAccountKey);
307 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
308 observer()->Clear();
309 } 281 }
310 282
311 virtual void TearDown() OVERRIDE { 283 virtual void TearDown() OVERRIDE {
312 account_tracker_->RemoveObserver(&observer_); 284 account_tracker_->RemoveObserver(&observer_);
313 account_tracker_->Shutdown(); 285 account_tracker_->Shutdown();
314 } 286 }
315 287
316 Profile* profile() {
317 return test_profile_.get();
318 }
319
320 AccountTrackerObserver* observer() { 288 AccountTrackerObserver* observer() {
321 return &observer_; 289 return &observer_;
322 } 290 }
323 291
324 AccountTracker* account_tracker() { 292 AccountTracker* account_tracker() {
325 return account_tracker_.get(); 293 return account_tracker_.get();
326 } 294 }
327 295
328 // Helpers to pass fake events to the tracker. 296 // Helpers to pass fake events to the tracker.
329 297
330 void NotifyRemoveAccount(const std::string& username) { 298 void NotifyLogin(const std::string account_key) {
331 #if !defined(OS_CHROMEOS) 299 identity_provider()->LogIn(account_key);
332 if (username == kPrimaryAccountKey)
333 fake_signin_manager_->SignOut();
334 else
335 account_tracker()->GoogleSignedOut(username);
336 #else
337 account_tracker()->GoogleSignedOut(username);
338 #endif
339 } 300 }
340 301
302 void NotifyLogout() { identity_provider()->LogOut(); }
303
341 void NotifyTokenAvailable(const std::string& username) { 304 void NotifyTokenAvailable(const std::string& username) {
342 fake_oauth2_token_service_->IssueRefreshTokenForUser(username, 305 fake_oauth2_token_service_->AddAccount(username);
343 "refresh_token");
344 #if !defined(OS_CHROMEOS)
345 if (username == kPrimaryAccountKey)
346 fake_signin_manager_->OnExternalSigninCompleted(username);
347 #endif
348 } 306 }
349 307
350 void NotifyTokenRevoked(const std::string& username) { 308 void NotifyTokenRevoked(const std::string& username) {
351 fake_oauth2_token_service_->IssueRefreshTokenForUser(username, 309 fake_oauth2_token_service_->RemoveAccount(username);
352 std::string());
353 } 310 }
354 311
355 // Helpers to fake access token and user info fetching 312 // Helpers to fake access token and user info fetching
356 void IssueAccessToken(const std::string& username) { 313 void IssueAccessToken(const std::string& username) {
357 fake_oauth2_token_service_->IssueAllTokensForAccount( 314 fake_oauth2_token_service_->IssueAllTokensForAccount(
358 username, "access_token-" + username, base::Time::Max()); 315 username, "access_token-" + username, base::Time::Max());
359 } 316 }
360 317
361 std::string GetValidTokenInfoResponse(const std::string account_key) { 318 std::string GetValidTokenInfoResponse(const std::string account_key) {
362 return std::string("{ \"id\": \"") + AccountKeyToObfuscatedId(account_key) + 319 return std::string("{ \"id\": \"") + AccountKeyToObfuscatedId(account_key) +
363 "\" }"; 320 "\" }";
364 } 321 }
365 322
366 void ReturnOAuthUrlFetchResults(int fetcher_id, 323 void ReturnOAuthUrlFetchResults(int fetcher_id,
367 net::HttpStatusCode response_code, 324 net::HttpStatusCode response_code,
368 const std::string& response_string); 325 const std::string& response_string);
369 326
370 void ReturnOAuthUrlFetchSuccess(const std::string& account_key); 327 void ReturnOAuthUrlFetchSuccess(const std::string& account_key);
371 void ReturnOAuthUrlFetchFailure(const std::string& account_key); 328 void ReturnOAuthUrlFetchFailure(const std::string& account_key);
372 329
330 void SetupPrimaryLogin() {
331 // Initial setup for tests that start with a signed in profile.
332 NotifyLogin(kPrimaryAccountKey);
333 NotifyTokenAvailable(kPrimaryAccountKey);
334 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
335 observer()->Clear();
336 }
337
338 std::string active_account_id() {
339 return identity_provider()->GetActiveAccountId();
340 }
341
373 private: 342 private:
374 scoped_ptr<TestingProfile> test_profile_; 343 FakeIdentityProvider* identity_provider() {
344 return static_cast<FakeIdentityProvider*>(
345 account_tracker_->identity_provider());
346 }
347
348 base::MessageLoopForIO message_loop_; // net:: stuff needs IO message loop.
375 net::TestURLFetcherFactory test_fetcher_factory_; 349 net::TestURLFetcherFactory test_fetcher_factory_;
376 FakeProfileOAuth2TokenService* fake_oauth2_token_service_; 350 scoped_ptr<FakeOAuth2TokenService> fake_oauth2_token_service_;
377 FakeSigninManagerForTesting* fake_signin_manager_;
378 content::TestBrowserThreadBundle thread_bundle_;
379 351
380 scoped_ptr<AccountTracker> account_tracker_; 352 scoped_ptr<AccountTracker> account_tracker_;
381 AccountTrackerObserver observer_; 353 AccountTrackerObserver observer_;
382 }; 354 };
383 355
384 void IdentityAccountTrackerTest::ReturnOAuthUrlFetchResults( 356 void IdentityAccountTrackerTest::ReturnOAuthUrlFetchResults(
385 int fetcher_id, 357 int fetcher_id,
386 net::HttpStatusCode response_code, 358 net::HttpStatusCode response_code,
387 const std::string& response_string) { 359 const std::string& response_string) {
388 360
(...skipping 13 matching lines...) Expand all
402 GetValidTokenInfoResponse(account_key)); 374 GetValidTokenInfoResponse(account_key));
403 } 375 }
404 376
405 void IdentityAccountTrackerTest::ReturnOAuthUrlFetchFailure( 377 void IdentityAccountTrackerTest::ReturnOAuthUrlFetchFailure(
406 const std::string& account_key) { 378 const std::string& account_key) {
407 IssueAccessToken(account_key); 379 IssueAccessToken(account_key);
408 ReturnOAuthUrlFetchResults( 380 ReturnOAuthUrlFetchResults(
409 gaia::GaiaOAuthClient::kUrlFetcherId, net::HTTP_BAD_REQUEST, ""); 381 gaia::GaiaOAuthClient::kUrlFetcherId, net::HTTP_BAD_REQUEST, "");
410 } 382 }
411 383
384 // Primary tests just involve the Active account
385
386 TEST_F(IdentityAccountTrackerTest, PrimaryNoEventsBeforeLogin) {
387 NotifyTokenAvailable(kPrimaryAccountKey);
388 NotifyTokenRevoked(kPrimaryAccountKey);
389 NotifyLogout();
390 EXPECT_TRUE(observer()->CheckEvents());
391 }
392
393 TEST_F(IdentityAccountTrackerTest, PrimaryLoginThenTokenAvailable) {
394 NotifyLogin(kPrimaryAccountKey);
395 NotifyTokenAvailable(kPrimaryAccountKey);
396 EXPECT_TRUE(observer()->CheckEvents());
397
398 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
399 EXPECT_TRUE(
400 observer()->CheckEvents(TrackingEvent(ADDED, kPrimaryAccountKey),
401 TrackingEvent(SIGN_IN, kPrimaryAccountKey)));
402 }
403
404 TEST_F(IdentityAccountTrackerTest, PrimaryTokenAvailableThenLogin) {
405 NotifyTokenAvailable(kPrimaryAccountKey);
406 EXPECT_TRUE(observer()->CheckEvents());
407
408 NotifyLogin(kPrimaryAccountKey);
409 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
410 EXPECT_TRUE(
411 observer()->CheckEvents(TrackingEvent(ADDED, kPrimaryAccountKey),
412 TrackingEvent(SIGN_IN, kPrimaryAccountKey)));
413 }
414
415 TEST_F(IdentityAccountTrackerTest, PrimaryTokenAvailableAndRevokedThenLogin) {
416 NotifyTokenAvailable(kPrimaryAccountKey);
417 EXPECT_TRUE(observer()->CheckEvents());
418
419 NotifyLogin(kPrimaryAccountKey);
420 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
421 EXPECT_TRUE(
422 observer()->CheckEvents(TrackingEvent(ADDED, kPrimaryAccountKey),
423 TrackingEvent(SIGN_IN, kPrimaryAccountKey)));
424 }
425
426 TEST_F(IdentityAccountTrackerTest, PrimaryRevokeThenLogout) {
427 NotifyLogin(kPrimaryAccountKey);
428 NotifyTokenAvailable(kPrimaryAccountKey);
429 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
430 observer()->Clear();
431
432 NotifyTokenRevoked(kPrimaryAccountKey);
433 EXPECT_TRUE(
434 observer()->CheckEvents(TrackingEvent(SIGN_OUT, kPrimaryAccountKey)));
435
436 NotifyLogout();
437 EXPECT_TRUE(
438 observer()->CheckEvents(TrackingEvent(REMOVED, kPrimaryAccountKey)));
439 }
440
441 TEST_F(IdentityAccountTrackerTest, PrimaryRevokeThenLogin) {
442 NotifyLogin(kPrimaryAccountKey);
443 NotifyTokenAvailable(kPrimaryAccountKey);
444 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
445 NotifyTokenRevoked(kPrimaryAccountKey);
446 observer()->Clear();
447
448 NotifyLogin(kPrimaryAccountKey);
449 EXPECT_TRUE(observer()->CheckEvents());
450 }
451
452 TEST_F(IdentityAccountTrackerTest, PrimaryRevokeThenTokenAvailable) {
453 NotifyLogin(kPrimaryAccountKey);
454 NotifyTokenAvailable(kPrimaryAccountKey);
455 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
456 NotifyTokenRevoked(kPrimaryAccountKey);
457 observer()->Clear();
458
459 NotifyTokenAvailable(kPrimaryAccountKey);
460 EXPECT_TRUE(
461 observer()->CheckEvents(TrackingEvent(SIGN_IN, kPrimaryAccountKey)));
462 }
463
464 TEST_F(IdentityAccountTrackerTest, PrimaryLogoutThenRevoke) {
465 NotifyLogin(kPrimaryAccountKey);
466 NotifyTokenAvailable(kPrimaryAccountKey);
467 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
468 observer()->Clear();
469
470 NotifyLogout();
471 EXPECT_TRUE(
472 observer()->CheckEvents(TrackingEvent(SIGN_OUT, kPrimaryAccountKey),
473 TrackingEvent(REMOVED, kPrimaryAccountKey)));
474
475 NotifyTokenRevoked(kPrimaryAccountKey);
476 EXPECT_TRUE(observer()->CheckEvents());
477 }
478
479 TEST_F(IdentityAccountTrackerTest, PrimaryLogoutFetchCancelAvailable) {
480 NotifyLogin(kPrimaryAccountKey);
481 NotifyTokenAvailable(kPrimaryAccountKey);
482 // TokenAvailable kicks off a fetch. Logout without satisfying it.
483 NotifyLogout();
484 EXPECT_TRUE(observer()->CheckEvents());
485
486 NotifyLogin(kPrimaryAccountKey);
487 NotifyTokenAvailable(kPrimaryAccountKey);
488 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
489 EXPECT_TRUE(observer()->CheckEvents(
490 TrackingEvent(ADDED, kPrimaryAccountKey),
491 TrackingEvent(SIGN_IN, kPrimaryAccountKey)));
492 }
493
494 // Non-primary accounts
495
412 TEST_F(IdentityAccountTrackerTest, Available) { 496 TEST_F(IdentityAccountTrackerTest, Available) {
497 SetupPrimaryLogin();
498
413 NotifyTokenAvailable("user@example.com"); 499 NotifyTokenAvailable("user@example.com");
414 EXPECT_TRUE(observer()->CheckEvents()); 500 EXPECT_TRUE(observer()->CheckEvents());
415 501
416 ReturnOAuthUrlFetchSuccess("user@example.com"); 502 ReturnOAuthUrlFetchSuccess("user@example.com");
417 EXPECT_TRUE(observer()->CheckEvents( 503 EXPECT_TRUE(observer()->CheckEvents(
418 TrackingEvent(ADDED, "user@example.com"), 504 TrackingEvent(ADDED, "user@example.com"),
419 TrackingEvent(SIGN_IN, "user@example.com"))); 505 TrackingEvent(SIGN_IN, "user@example.com")));
420 } 506 }
421 507
422 TEST_F(IdentityAccountTrackerTest, Revoke) { 508 TEST_F(IdentityAccountTrackerTest, Revoke) {
509 SetupPrimaryLogin();
510
423 account_tracker()->OnRefreshTokenRevoked("user@example.com"); 511 account_tracker()->OnRefreshTokenRevoked("user@example.com");
424 EXPECT_TRUE(observer()->CheckEvents()); 512 EXPECT_TRUE(observer()->CheckEvents());
425 } 513 }
426 514
427 TEST_F(IdentityAccountTrackerTest, Remove) { 515 TEST_F(IdentityAccountTrackerTest, AvailableRevokeAvailable) {
428 NotifyRemoveAccount("user@example.com"); 516 SetupPrimaryLogin();
429 EXPECT_TRUE(observer()->CheckEvents());
430 }
431
432 TEST_F(IdentityAccountTrackerTest, AvailableRemoveFetchCancelAvailable) {
433 NotifyTokenAvailable("user@example.com");
434 NotifyRemoveAccount("user@example.com");
435 EXPECT_TRUE(observer()->CheckEvents());
436 517
437 NotifyTokenAvailable("user@example.com"); 518 NotifyTokenAvailable("user@example.com");
438 ReturnOAuthUrlFetchSuccess("user@example.com"); 519 ReturnOAuthUrlFetchSuccess("user@example.com");
439 EXPECT_TRUE(observer()->CheckEvents(
440 TrackingEvent(ADDED, "user@example.com"),
441 TrackingEvent(SIGN_IN, "user@example.com")));
442 }
443
444 TEST_F(IdentityAccountTrackerTest, AvailableRemoveAvailable) {
445 NotifyTokenAvailable("user@example.com");
446 ReturnOAuthUrlFetchSuccess("user@example.com");
447 NotifyRemoveAccount("user@example.com");
448 EXPECT_TRUE(observer()->CheckEvents(
449 TrackingEvent(ADDED, "user@example.com"),
450 TrackingEvent(SIGN_IN, "user@example.com"),
451 TrackingEvent(SIGN_OUT, "user@example.com"),
452 TrackingEvent(REMOVED, "user@example.com")));
453
454 NotifyTokenAvailable("user@example.com");
455 ReturnOAuthUrlFetchSuccess("user@example.com");
456 EXPECT_TRUE(observer()->CheckEvents(
457 TrackingEvent(ADDED, "user@example.com"),
458 TrackingEvent(SIGN_IN, "user@example.com")));
459 }
460
461 TEST_F(IdentityAccountTrackerTest, AvailableRevokeAvailable) {
462 NotifyTokenAvailable("user@example.com");
463 ReturnOAuthUrlFetchSuccess("user@example.com");
464 NotifyTokenRevoked("user@example.com"); 520 NotifyTokenRevoked("user@example.com");
465 EXPECT_TRUE(observer()->CheckEvents( 521 EXPECT_TRUE(observer()->CheckEvents(
466 TrackingEvent(ADDED, "user@example.com"), 522 TrackingEvent(ADDED, "user@example.com"),
467 TrackingEvent(SIGN_IN, "user@example.com"), 523 TrackingEvent(SIGN_IN, "user@example.com"),
468 TrackingEvent(SIGN_OUT, "user@example.com"))); 524 TrackingEvent(SIGN_OUT, "user@example.com")));
469 525
470 NotifyTokenAvailable("user@example.com"); 526 NotifyTokenAvailable("user@example.com");
471 EXPECT_TRUE(observer()->CheckEvents( 527 EXPECT_TRUE(observer()->CheckEvents(
472 TrackingEvent(SIGN_IN, "user@example.com"))); 528 TrackingEvent(SIGN_IN, "user@example.com")));
473 } 529 }
474 530
475 TEST_F(IdentityAccountTrackerTest, AvailableRevokeAvailableWithPendingFetch) { 531 TEST_F(IdentityAccountTrackerTest, AvailableRevokeAvailableWithPendingFetch) {
532 SetupPrimaryLogin();
533
476 NotifyTokenAvailable("user@example.com"); 534 NotifyTokenAvailable("user@example.com");
477 NotifyTokenRevoked("user@example.com"); 535 NotifyTokenRevoked("user@example.com");
478 EXPECT_TRUE(observer()->CheckEvents()); 536 EXPECT_TRUE(observer()->CheckEvents());
479 537
480 NotifyTokenAvailable("user@example.com"); 538 NotifyTokenAvailable("user@example.com");
481 ReturnOAuthUrlFetchSuccess("user@example.com"); 539 ReturnOAuthUrlFetchSuccess("user@example.com");
482 EXPECT_TRUE(observer()->CheckEvents( 540 EXPECT_TRUE(observer()->CheckEvents(
483 TrackingEvent(ADDED, "user@example.com"), 541 TrackingEvent(ADDED, "user@example.com"),
484 TrackingEvent(SIGN_IN, "user@example.com"))); 542 TrackingEvent(SIGN_IN, "user@example.com")));
485 } 543 }
486 544
487 TEST_F(IdentityAccountTrackerTest, AvailableRevokeRemove) { 545 TEST_F(IdentityAccountTrackerTest, AvailableRevokeRevoke) {
546 SetupPrimaryLogin();
547
488 NotifyTokenAvailable("user@example.com"); 548 NotifyTokenAvailable("user@example.com");
489 ReturnOAuthUrlFetchSuccess("user@example.com"); 549 ReturnOAuthUrlFetchSuccess("user@example.com");
490 NotifyTokenRevoked("user@example.com"); 550 NotifyTokenRevoked("user@example.com");
491 EXPECT_TRUE(observer()->CheckEvents(
492 TrackingEvent(ADDED, "user@example.com"),
493 TrackingEvent(SIGN_IN, "user@example.com"),
494 TrackingEvent(SIGN_OUT, "user@example.com")));
495
496 NotifyRemoveAccount("user@example.com");
497 EXPECT_TRUE(observer()->CheckEvents(
498 TrackingEvent(REMOVED, "user@example.com")));
499 }
500
501 TEST_F(IdentityAccountTrackerTest, AvailableRevokeRevoke) {
502 NotifyTokenAvailable("user@example.com");
503 ReturnOAuthUrlFetchSuccess("user@example.com");
504 NotifyTokenRevoked("user@example.com");
505 EXPECT_TRUE(observer()->CheckEvents( 551 EXPECT_TRUE(observer()->CheckEvents(
506 TrackingEvent(ADDED, "user@example.com"), 552 TrackingEvent(ADDED, "user@example.com"),
507 TrackingEvent(SIGN_IN, "user@example.com"), 553 TrackingEvent(SIGN_IN, "user@example.com"),
508 TrackingEvent(SIGN_OUT, "user@example.com"))); 554 TrackingEvent(SIGN_OUT, "user@example.com")));
509 555
510 NotifyTokenRevoked("user@example.com"); 556 NotifyTokenRevoked("user@example.com");
511 EXPECT_TRUE(observer()->CheckEvents()); 557 EXPECT_TRUE(observer()->CheckEvents());
512 } 558 }
513 559
514 TEST_F(IdentityAccountTrackerTest, AvailableAvailable) { 560 TEST_F(IdentityAccountTrackerTest, AvailableAvailable) {
561 SetupPrimaryLogin();
562
515 NotifyTokenAvailable("user@example.com"); 563 NotifyTokenAvailable("user@example.com");
516 ReturnOAuthUrlFetchSuccess("user@example.com"); 564 ReturnOAuthUrlFetchSuccess("user@example.com");
517 EXPECT_TRUE(observer()->CheckEvents( 565 EXPECT_TRUE(observer()->CheckEvents(
518 TrackingEvent(ADDED, "user@example.com"), 566 TrackingEvent(ADDED, "user@example.com"),
519 TrackingEvent(SIGN_IN, "user@example.com"))); 567 TrackingEvent(SIGN_IN, "user@example.com")));
520 568
521 NotifyTokenAvailable("user@example.com"); 569 NotifyTokenAvailable("user@example.com");
522 EXPECT_TRUE(observer()->CheckEvents()); 570 EXPECT_TRUE(observer()->CheckEvents());
523 } 571 }
524 572
525 TEST_F(IdentityAccountTrackerTest, TwoAccounts) { 573 TEST_F(IdentityAccountTrackerTest, TwoAccounts) {
574 SetupPrimaryLogin();
575
526 NotifyTokenAvailable("alpha@example.com"); 576 NotifyTokenAvailable("alpha@example.com");
527 ReturnOAuthUrlFetchSuccess("alpha@example.com"); 577 ReturnOAuthUrlFetchSuccess("alpha@example.com");
528 EXPECT_TRUE(observer()->CheckEvents( 578 EXPECT_TRUE(observer()->CheckEvents(
529 TrackingEvent(ADDED, "alpha@example.com"), 579 TrackingEvent(ADDED, "alpha@example.com"),
530 TrackingEvent(SIGN_IN, "alpha@example.com"))); 580 TrackingEvent(SIGN_IN, "alpha@example.com")));
531 581
532 NotifyTokenAvailable("beta@example.com"); 582 NotifyTokenAvailable("beta@example.com");
533 ReturnOAuthUrlFetchSuccess("beta@example.com"); 583 ReturnOAuthUrlFetchSuccess("beta@example.com");
534 EXPECT_TRUE(observer()->CheckEvents( 584 EXPECT_TRUE(observer()->CheckEvents(
535 TrackingEvent(ADDED, "beta@example.com"), 585 TrackingEvent(ADDED, "beta@example.com"),
536 TrackingEvent(SIGN_IN, "beta@example.com"))); 586 TrackingEvent(SIGN_IN, "beta@example.com")));
537 587
538 NotifyRemoveAccount("alpha@example.com"); 588 NotifyTokenRevoked("alpha@example.com");
539 EXPECT_TRUE(observer()->CheckEvents( 589 EXPECT_TRUE(
540 TrackingEvent(SIGN_OUT, "alpha@example.com"), 590 observer()->CheckEvents(TrackingEvent(SIGN_OUT, "alpha@example.com")));
541 TrackingEvent(REMOVED, "alpha@example.com")));
542 591
543 NotifyRemoveAccount("beta@example.com"); 592 NotifyTokenRevoked("beta@example.com");
544 EXPECT_TRUE(observer()->CheckEvents(
545 TrackingEvent(SIGN_OUT, "beta@example.com"),
546 TrackingEvent(REMOVED, "beta@example.com")));
547 }
548
549 TEST_F(IdentityAccountTrackerTest, GlobalErrors) {
550 NotifyTokenAvailable("alpha@example.com");
551 ReturnOAuthUrlFetchSuccess("alpha@example.com");
552 EXPECT_TRUE(observer()->CheckEvents(
553 TrackingEvent(ADDED, "alpha@example.com"),
554 TrackingEvent(SIGN_IN, "alpha@example.com")));
555 NotifyTokenAvailable("beta@example.com");
556 ReturnOAuthUrlFetchSuccess("beta@example.com");
557 EXPECT_TRUE(observer()->CheckEvents(
558 TrackingEvent(ADDED, "beta@example.com"),
559 TrackingEvent(SIGN_IN, "beta@example.com")));
560
561 EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
562 account_tracker()->GetAuthStatus());
563
564 account_tracker()->ReportAuthError(
565 "beta@example.com",
566 GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
567 EXPECT_TRUE(observer()->CheckEvents( 593 EXPECT_TRUE(observer()->CheckEvents(
568 TrackingEvent(SIGN_OUT, "beta@example.com"))); 594 TrackingEvent(SIGN_OUT, "beta@example.com")));
569 EXPECT_EQ(GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED),
570 account_tracker()->GetAuthStatus());
571
572 account_tracker()->ReportAuthError(
573 "alpha@example.com",
574 GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
575 EXPECT_TRUE(observer()->CheckEvents(
576 TrackingEvent(SIGN_OUT, "alpha@example.com")));
577 EXPECT_EQ(GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED),
578 account_tracker()->GetAuthStatus());
579
580 NotifyRemoveAccount("alpha@example.com");
581 EXPECT_EQ(GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED),
582 account_tracker()->GetAuthStatus());
583
584 NotifyTokenAvailable("beta@example.com");
585 EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
586 account_tracker()->GetAuthStatus());
587 } 595 }
588 596
589 TEST_F(IdentityAccountTrackerTest, AvailableTokenFetchFailAvailable) { 597 TEST_F(IdentityAccountTrackerTest, AvailableTokenFetchFailAvailable) {
590 NotifyTokenAvailable("alpha@example.com"); 598 SetupPrimaryLogin();
591 ReturnOAuthUrlFetchFailure("alpha@example.com"); 599
600 NotifyTokenAvailable("user@example.com");
601 ReturnOAuthUrlFetchFailure("user@example.com");
592 EXPECT_TRUE(observer()->CheckEvents()); 602 EXPECT_TRUE(observer()->CheckEvents());
593 603
594 NotifyTokenAvailable("user@example.com"); 604 NotifyTokenAvailable("user@example.com");
595 ReturnOAuthUrlFetchSuccess("user@example.com"); 605 ReturnOAuthUrlFetchSuccess("user@example.com");
596 EXPECT_TRUE(observer()->CheckEvents( 606 EXPECT_TRUE(observer()->CheckEvents(
597 TrackingEvent(ADDED, "user@example.com"), 607 TrackingEvent(ADDED, "user@example.com"),
598 TrackingEvent(SIGN_IN, "user@example.com"))); 608 TrackingEvent(SIGN_IN, "user@example.com")));
599 } 609 }
600 610
601 // The Chrome OS fake sign-in manager doesn't do sign-in or sign-out. 611 TEST_F(IdentityAccountTrackerTest, MultiSignOutSignIn) {
602 #if !defined(OS_CHROMEOS) 612 SetupPrimaryLogin();
603 613
604 TEST_F(IdentityAccountTrackerTest, PrimarySignOutSignIn) {
605 NotifyRemoveAccount(kPrimaryAccountKey);
606 EXPECT_TRUE(observer()->CheckEvents(
607 TrackingEvent(SIGN_OUT, kPrimaryAccountKey),
608 TrackingEvent(REMOVED, kPrimaryAccountKey)));
609
610 NotifyTokenAvailable(kPrimaryAccountKey);
611 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
612 EXPECT_TRUE(observer()->CheckEvents(
613 TrackingEvent(ADDED, kPrimaryAccountKey),
614 TrackingEvent(SIGN_IN, kPrimaryAccountKey)));
615 }
616
617 TEST_F(IdentityAccountTrackerTest, PrimarySignOutSignInTwoAccounts) {
618 NotifyTokenAvailable("alpha@example.com"); 614 NotifyTokenAvailable("alpha@example.com");
619 ReturnOAuthUrlFetchSuccess("alpha@example.com"); 615 ReturnOAuthUrlFetchSuccess("alpha@example.com");
620 NotifyTokenAvailable("beta@example.com"); 616 NotifyTokenAvailable("beta@example.com");
621 ReturnOAuthUrlFetchSuccess("beta@example.com"); 617 ReturnOAuthUrlFetchSuccess("beta@example.com");
622 618
623 observer()->SortEventsByUser(); 619 observer()->SortEventsByUser();
624 EXPECT_TRUE(observer()->CheckEvents( 620 EXPECT_TRUE(observer()->CheckEvents(
625 TrackingEvent(ADDED, "alpha@example.com"), 621 TrackingEvent(ADDED, "alpha@example.com"),
626 TrackingEvent(SIGN_IN, "alpha@example.com"), 622 TrackingEvent(SIGN_IN, "alpha@example.com"),
627 TrackingEvent(ADDED, "beta@example.com"), 623 TrackingEvent(ADDED, "beta@example.com"),
628 TrackingEvent(SIGN_IN, "beta@example.com"))); 624 TrackingEvent(SIGN_IN, "beta@example.com")));
629 625
630 NotifyRemoveAccount(kPrimaryAccountKey); 626 NotifyLogout();
631 observer()->SortEventsByUser(); 627 observer()->SortEventsByUser();
632 EXPECT_TRUE(observer()->CheckEvents( 628 EXPECT_TRUE(observer()->CheckEvents(
633 TrackingEvent(SIGN_OUT, "alpha@example.com"), 629 TrackingEvent(SIGN_OUT, "alpha@example.com"),
634 TrackingEvent(REMOVED, "alpha@example.com"), 630 TrackingEvent(REMOVED, "alpha@example.com"),
635 TrackingEvent(SIGN_OUT, "beta@example.com"), 631 TrackingEvent(SIGN_OUT, "beta@example.com"),
636 TrackingEvent(REMOVED, "beta@example.com"), 632 TrackingEvent(REMOVED, "beta@example.com"),
637 TrackingEvent(SIGN_OUT, kPrimaryAccountKey), 633 TrackingEvent(SIGN_OUT, kPrimaryAccountKey),
638 TrackingEvent(REMOVED, kPrimaryAccountKey))); 634 TrackingEvent(REMOVED, kPrimaryAccountKey)));
639 635
640 // No events fire at all while profile is signed out. 636 // No events fire at all while profile is signed out.
641 NotifyTokenRevoked("alpha@example.com"); 637 NotifyTokenRevoked("alpha@example.com");
642 NotifyTokenAvailable("gamma@example.com"); 638 NotifyTokenAvailable("gamma@example.com");
643 EXPECT_TRUE(observer()->CheckEvents()); 639 EXPECT_TRUE(observer()->CheckEvents());
644 640
645 // Signing the profile in again will resume tracking all accounts. 641 // Signing the profile in again will resume tracking all accounts.
642 NotifyLogin(kPrimaryAccountKey);
646 NotifyTokenAvailable(kPrimaryAccountKey); 643 NotifyTokenAvailable(kPrimaryAccountKey);
647 ReturnOAuthUrlFetchSuccess("beta@example.com"); 644 ReturnOAuthUrlFetchSuccess("beta@example.com");
648 ReturnOAuthUrlFetchSuccess("gamma@example.com"); 645 ReturnOAuthUrlFetchSuccess("gamma@example.com");
649 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey); 646 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
650 observer()->SortEventsByUser(); 647 observer()->SortEventsByUser();
651 EXPECT_TRUE(observer()->CheckEvents( 648 EXPECT_TRUE(observer()->CheckEvents(
652 TrackingEvent(ADDED, "beta@example.com"), 649 TrackingEvent(ADDED, "beta@example.com"),
653 TrackingEvent(SIGN_IN, "beta@example.com"), 650 TrackingEvent(SIGN_IN, "beta@example.com"),
654 TrackingEvent(ADDED, "gamma@example.com"), 651 TrackingEvent(ADDED, "gamma@example.com"),
655 TrackingEvent(SIGN_IN, "gamma@example.com"), 652 TrackingEvent(SIGN_IN, "gamma@example.com"),
656 TrackingEvent(ADDED, kPrimaryAccountKey), 653 TrackingEvent(ADDED, kPrimaryAccountKey),
657 TrackingEvent(SIGN_IN, kPrimaryAccountKey))); 654 TrackingEvent(SIGN_IN, kPrimaryAccountKey)));
658 655
659 // Revoking the primary token does not affect other accounts. 656 // Revoking the primary token does not affect other accounts.
660 NotifyTokenRevoked(kPrimaryAccountKey); 657 NotifyTokenRevoked(kPrimaryAccountKey);
661 EXPECT_TRUE(observer()->CheckEvents( 658 EXPECT_TRUE(observer()->CheckEvents(
662 TrackingEvent(SIGN_OUT, kPrimaryAccountKey))); 659 TrackingEvent(SIGN_OUT, kPrimaryAccountKey)));
663 660
664 NotifyTokenAvailable(kPrimaryAccountKey); 661 NotifyTokenAvailable(kPrimaryAccountKey);
665 EXPECT_TRUE(observer()->CheckEvents( 662 EXPECT_TRUE(observer()->CheckEvents(
666 TrackingEvent(SIGN_IN, kPrimaryAccountKey))); 663 TrackingEvent(SIGN_IN, kPrimaryAccountKey)));
667 } 664 }
668 665
669 #endif // !defined(OS_CHROMEOS) 666 // Primary/non-primary interactions
667
668 TEST_F(IdentityAccountTrackerTest, MultiNoEventsBeforeLogin) {
669 NotifyTokenAvailable(kPrimaryAccountKey);
670 NotifyTokenAvailable("user@example.com");
671 NotifyTokenRevoked("user@example.com");
672 NotifyTokenRevoked(kPrimaryAccountKey);
673 NotifyLogout();
674 EXPECT_TRUE(observer()->CheckEvents());
675 }
676
677 TEST_F(IdentityAccountTrackerTest, MultiLogoutRemovesAllAccounts) {
678 NotifyLogin(kPrimaryAccountKey);
679 NotifyTokenAvailable(kPrimaryAccountKey);
680 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
681 NotifyTokenAvailable("user@example.com");
682 ReturnOAuthUrlFetchSuccess("user@example.com");
683 observer()->Clear();
684
685 NotifyLogout();
686 observer()->SortEventsByUser();
687 EXPECT_TRUE(
688 observer()->CheckEvents(TrackingEvent(SIGN_OUT, kPrimaryAccountKey),
689 TrackingEvent(REMOVED, kPrimaryAccountKey),
690 TrackingEvent(SIGN_OUT, "user@example.com"),
691 TrackingEvent(REMOVED, "user@example.com")));
692 }
693
694 TEST_F(IdentityAccountTrackerTest, MultiRevokePrimaryDoesNotRemoveAllAccounts) {
695 NotifyLogin(kPrimaryAccountKey);
696 NotifyTokenAvailable(kPrimaryAccountKey);
697 ReturnOAuthUrlFetchSuccess(kPrimaryAccountKey);
698 NotifyTokenAvailable("user@example.com");
699 ReturnOAuthUrlFetchSuccess("user@example.com");
700 observer()->Clear();
701
702 NotifyTokenRevoked(kPrimaryAccountKey);
703 observer()->SortEventsByUser();
704 EXPECT_TRUE(
705 observer()->CheckEvents(TrackingEvent(SIGN_OUT, kPrimaryAccountKey)));
706 }
670 707
671 TEST_F(IdentityAccountTrackerTest, GetAccountsPrimary) { 708 TEST_F(IdentityAccountTrackerTest, GetAccountsPrimary) {
709 SetupPrimaryLogin();
710
672 std::vector<AccountIds> ids = account_tracker()->GetAccounts(); 711 std::vector<AccountIds> ids = account_tracker()->GetAccounts();
673 EXPECT_EQ(1ul, ids.size()); 712 EXPECT_EQ(1ul, ids.size());
674 EXPECT_EQ(kPrimaryAccountKey, ids[0].account_key); 713 EXPECT_EQ(kPrimaryAccountKey, ids[0].account_key);
675 EXPECT_EQ(AccountKeyToObfuscatedId(kPrimaryAccountKey), ids[0].gaia); 714 EXPECT_EQ(AccountKeyToObfuscatedId(kPrimaryAccountKey), ids[0].gaia);
676 } 715 }
677 716
678 TEST_F(IdentityAccountTrackerTest, GetAccountsSignedOut) { 717 TEST_F(IdentityAccountTrackerTest, GetAccountsSignedOut) {
679 NotifyTokenRevoked(kPrimaryAccountKey);
680
681 std::vector<AccountIds> ids = account_tracker()->GetAccounts(); 718 std::vector<AccountIds> ids = account_tracker()->GetAccounts();
682 EXPECT_EQ(0ul, ids.size()); 719 EXPECT_EQ(0ul, ids.size());
683 } 720 }
684 721
685 TEST_F(IdentityAccountTrackerTest, GetAccountsOnlyReturnAccountsWithTokens) { 722 TEST_F(IdentityAccountTrackerTest, GetAccountsOnlyReturnAccountsWithTokens) {
723 SetupPrimaryLogin();
724
686 NotifyTokenAvailable("alpha@example.com"); 725 NotifyTokenAvailable("alpha@example.com");
687 NotifyTokenAvailable("beta@example.com"); 726 NotifyTokenAvailable("beta@example.com");
688 ReturnOAuthUrlFetchSuccess("beta@example.com"); 727 ReturnOAuthUrlFetchSuccess("beta@example.com");
689 728
690 std::vector<AccountIds> ids = account_tracker()->GetAccounts(); 729 std::vector<AccountIds> ids = account_tracker()->GetAccounts();
691 EXPECT_EQ(2ul, ids.size()); 730 EXPECT_EQ(2ul, ids.size());
692 EXPECT_EQ(kPrimaryAccountKey, ids[0].account_key); 731 EXPECT_EQ(kPrimaryAccountKey, ids[0].account_key);
693 EXPECT_EQ(AccountKeyToObfuscatedId(kPrimaryAccountKey), ids[0].gaia); 732 EXPECT_EQ(AccountKeyToObfuscatedId(kPrimaryAccountKey), ids[0].gaia);
694 EXPECT_EQ("beta@example.com", ids[1].account_key); 733 EXPECT_EQ("beta@example.com", ids[1].account_key);
695 EXPECT_EQ(AccountKeyToObfuscatedId("beta@example.com"), ids[1].gaia); 734 EXPECT_EQ(AccountKeyToObfuscatedId("beta@example.com"), ids[1].gaia);
696 } 735 }
697 736
698 TEST_F(IdentityAccountTrackerTest, GetAccountsSortOrder) { 737 TEST_F(IdentityAccountTrackerTest, GetAccountsSortOrder) {
738 SetupPrimaryLogin();
739
699 NotifyTokenAvailable("zeta@example.com"); 740 NotifyTokenAvailable("zeta@example.com");
700 ReturnOAuthUrlFetchSuccess("zeta@example.com"); 741 ReturnOAuthUrlFetchSuccess("zeta@example.com");
701 NotifyTokenAvailable("alpha@example.com"); 742 NotifyTokenAvailable("alpha@example.com");
702 ReturnOAuthUrlFetchSuccess("alpha@example.com"); 743 ReturnOAuthUrlFetchSuccess("alpha@example.com");
703 744
704 // The primary account will be first in the vector. Remaining accounts 745 // The primary account will be first in the vector. Remaining accounts
705 // will be sorted by gaia ID. 746 // will be sorted by gaia ID.
706 std::vector<AccountIds> ids = account_tracker()->GetAccounts(); 747 std::vector<AccountIds> ids = account_tracker()->GetAccounts();
707 EXPECT_EQ(3ul, ids.size()); 748 EXPECT_EQ(3ul, ids.size());
708 EXPECT_EQ(kPrimaryAccountKey, ids[0].account_key); 749 EXPECT_EQ(kPrimaryAccountKey, ids[0].account_key);
709 EXPECT_EQ(AccountKeyToObfuscatedId(kPrimaryAccountKey), ids[0].gaia); 750 EXPECT_EQ(AccountKeyToObfuscatedId(kPrimaryAccountKey), ids[0].gaia);
710 EXPECT_EQ("alpha@example.com", ids[1].account_key); 751 EXPECT_EQ("alpha@example.com", ids[1].account_key);
711 EXPECT_EQ(AccountKeyToObfuscatedId("alpha@example.com"), ids[1].gaia); 752 EXPECT_EQ(AccountKeyToObfuscatedId("alpha@example.com"), ids[1].gaia);
712 EXPECT_EQ("zeta@example.com", ids[2].account_key); 753 EXPECT_EQ("zeta@example.com", ids[2].account_key);
713 EXPECT_EQ(AccountKeyToObfuscatedId("zeta@example.com"), ids[2].gaia); 754 EXPECT_EQ(AccountKeyToObfuscatedId("zeta@example.com"), ids[2].gaia);
714 } 755 }
715 756
716 TEST_F(IdentityAccountTrackerTest, 757 TEST_F(IdentityAccountTrackerTest,
717 GetAccountsReturnNothingWhenPrimarySignedOut) { 758 GetAccountsReturnNothingWhenPrimarySignedOut) {
759 SetupPrimaryLogin();
760
718 NotifyTokenAvailable("zeta@example.com"); 761 NotifyTokenAvailable("zeta@example.com");
719 ReturnOAuthUrlFetchSuccess("zeta@example.com"); 762 ReturnOAuthUrlFetchSuccess("zeta@example.com");
720 NotifyTokenAvailable("alpha@example.com"); 763 NotifyTokenAvailable("alpha@example.com");
721 ReturnOAuthUrlFetchSuccess("alpha@example.com"); 764 ReturnOAuthUrlFetchSuccess("alpha@example.com");
722 765
723 NotifyTokenRevoked(kPrimaryAccountKey); 766 NotifyTokenRevoked(kPrimaryAccountKey);
724 767
725 std::vector<AccountIds> ids = account_tracker()->GetAccounts(); 768 std::vector<AccountIds> ids = account_tracker()->GetAccounts();
726 EXPECT_EQ(0ul, ids.size()); 769 EXPECT_EQ(0ul, ids.size());
727 } 770 }
728 771
729 } // namespace extensions 772 } // namespace gaia
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698