Home | History | Annotate | Download | only in signin
      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
      3 // found in the LICENSE file.
      4 
      5 #include "base/command_line.h"
      6 #include "base/memory/scoped_ptr.h"
      7 #include "base/run_loop.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "base/time/time.h"
     10 #include "build/build_config.h"
     11 #include "chrome/browser/prefs/pref_service_syncable.h"
     12 #include "chrome/browser/signin/account_reconcilor_factory.h"
     13 #include "chrome/browser/signin/chrome_signin_client_factory.h"
     14 #include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
     15 #include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
     16 #include "chrome/browser/signin/fake_signin_manager.h"
     17 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
     18 #include "chrome/browser/signin/signin_manager_factory.h"
     19 #include "chrome/browser/signin/test_signin_client_builder.h"
     20 #include "chrome/test/base/testing_browser_process.h"
     21 #include "chrome/test/base/testing_profile.h"
     22 #include "chrome/test/base/testing_profile_manager.h"
     23 #include "chrome/test/base/uma_histogram_helper.h"
     24 #include "components/signin/core/browser/account_reconcilor.h"
     25 #include "components/signin/core/browser/profile_oauth2_token_service.h"
     26 #include "components/signin/core/browser/signin_manager.h"
     27 #include "components/signin/core/browser/signin_metrics.h"
     28 #include "components/signin/core/common/profile_management_switches.h"
     29 #include "components/signin/core/common/signin_switches.h"
     30 #include "content/public/test/test_browser_thread_bundle.h"
     31 #include "google_apis/gaia/gaia_urls.h"
     32 #include "net/url_request/test_url_fetcher_factory.h"
     33 #include "testing/gmock/include/gmock/gmock.h"
     34 #include "testing/gtest/include/gtest/gtest.h"
     35 
     36 namespace {
     37 
     38 const char kTestEmail[] = "user (at) gmail.com";
     39 const char* const kHistogramsToSnapshot[] = {
     40     "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
     41     "Signin.Reconciler.AddedToCookieJar.FirstRun",
     42     "Signin.Reconciler.AddedToChrome.FirstRun",
     43     "Signin.Reconciler.DifferentPrimaryAccounts.SubsequentRun",
     44     "Signin.Reconciler.AddedToCookieJar.SubsequentRun",
     45     "Signin.Reconciler.AddedToChrome.SubsequentRun"};
     46 
     47 class MockAccountReconcilor : public testing::StrictMock<AccountReconcilor> {
     48  public:
     49   static KeyedService* Build(content::BrowserContext* context);
     50 
     51   MockAccountReconcilor(ProfileOAuth2TokenService* token_service,
     52                         SigninManagerBase* signin_manager,
     53                         SigninClient* client);
     54   virtual ~MockAccountReconcilor() {}
     55 
     56   MOCK_METHOD1(PerformMergeAction, void(const std::string& account_id));
     57   MOCK_METHOD1(PerformStartRemoveAction, void(const std::string& account_id));
     58   MOCK_METHOD3(
     59       PerformFinishRemoveAction,
     60       void(const std::string& account_id,
     61            const GoogleServiceAuthError& error,
     62            const std::vector<std::pair<std::string, bool> >& accounts));
     63   MOCK_METHOD2(PerformAddToChromeAction, void(const std::string& account_id,
     64                                               int session_index));
     65   MOCK_METHOD0(PerformLogoutAllAccountsAction, void());
     66 };
     67 
     68 // static
     69 KeyedService* MockAccountReconcilor::Build(content::BrowserContext* context) {
     70   Profile* profile = Profile::FromBrowserContext(context);
     71   AccountReconcilor* reconcilor = new MockAccountReconcilor(
     72       ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
     73       SigninManagerFactory::GetForProfile(profile),
     74       ChromeSigninClientFactory::GetForProfile(profile));
     75   reconcilor->Initialize(false /* start_reconcile_if_tokens_available */);
     76   return reconcilor;
     77 }
     78 
     79 MockAccountReconcilor::MockAccountReconcilor(
     80     ProfileOAuth2TokenService* token_service,
     81     SigninManagerBase* signin_manager,
     82     SigninClient* client)
     83     : testing::StrictMock<AccountReconcilor>(token_service,
     84                                              signin_manager,
     85                                              client) {}
     86 
     87 }  // namespace
     88 
     89 class AccountReconcilorTest : public ::testing::TestWithParam<bool> {
     90  public:
     91   AccountReconcilorTest();
     92   virtual void SetUp() OVERRIDE;
     93 
     94   TestingProfile* profile() { return profile_; }
     95   FakeSigninManagerForTesting* signin_manager() { return signin_manager_; }
     96   FakeProfileOAuth2TokenService* token_service() { return token_service_; }
     97   UMAHistogramHelper* histogram_helper() { return &histogram_helper_; }
     98 
     99   void SetFakeResponse(const std::string& url,
    100                        const std::string& data,
    101                        net::HttpStatusCode code,
    102                        net::URLRequestStatus::Status status) {
    103     url_fetcher_factory_.SetFakeResponse(GURL(url), data, code, status);
    104   }
    105 
    106   MockAccountReconcilor* GetMockReconcilor();
    107 
    108   void SimulateMergeSessionCompleted(
    109       MergeSessionHelper::Observer* observer,
    110       const std::string& account_id,
    111       const GoogleServiceAuthError& error);
    112 
    113   void SimulateRefreshTokenFetched(
    114       AccountReconcilor* reconcilor,
    115       const std::string& account_id,
    116       const std::string& refresh_token);
    117 
    118  private:
    119   content::TestBrowserThreadBundle bundle_;
    120   TestingProfile* profile_;
    121   FakeSigninManagerForTesting* signin_manager_;
    122   FakeProfileOAuth2TokenService* token_service_;
    123   MockAccountReconcilor* mock_reconcilor_;
    124   net::FakeURLFetcherFactory url_fetcher_factory_;
    125   scoped_ptr<TestingProfileManager> testing_profile_manager_;
    126   UMAHistogramHelper histogram_helper_;
    127 
    128   DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTest);
    129 };
    130 
    131 AccountReconcilorTest::AccountReconcilorTest()
    132     : signin_manager_(NULL),
    133       token_service_(NULL),
    134       mock_reconcilor_(NULL),
    135       url_fetcher_factory_(NULL) {}
    136 
    137 void AccountReconcilorTest::SetUp() {
    138   // If it's a non-parameterized test, or we have a parameter of true, set flag.
    139   if (!::testing::UnitTest::GetInstance()->current_test_info()->value_param() ||
    140       GetParam()) {
    141     CommandLine::ForCurrentProcess()->AppendSwitch(
    142         switches::kEnableNewProfileManagement);
    143   }
    144 
    145   testing_profile_manager_.reset(
    146       new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
    147   ASSERT_TRUE(testing_profile_manager_.get()->SetUp());
    148 
    149   TestingProfile::TestingFactories factories;
    150   factories.push_back(std::make_pair(ChromeSigninClientFactory::GetInstance(),
    151       signin::BuildTestSigninClient));
    152   factories.push_back(std::make_pair(
    153       ProfileOAuth2TokenServiceFactory::GetInstance(),
    154       BuildFakeProfileOAuth2TokenService));
    155   factories.push_back(std::make_pair(SigninManagerFactory::GetInstance(),
    156       FakeSigninManagerBase::Build));
    157   factories.push_back(std::make_pair(AccountReconcilorFactory::GetInstance(),
    158       MockAccountReconcilor::Build));
    159 
    160   profile_ = testing_profile_manager_.get()->CreateTestingProfile("name",
    161                               scoped_ptr<PrefServiceSyncable>(),
    162                               base::UTF8ToUTF16("name"), 0, std::string(),
    163                               factories);
    164 
    165   signin_manager_ =
    166       static_cast<FakeSigninManagerForTesting*>(
    167           SigninManagerFactory::GetForProfile(profile()));
    168 
    169   token_service_ =
    170       static_cast<FakeProfileOAuth2TokenService*>(
    171           ProfileOAuth2TokenServiceFactory::GetForProfile(profile()));
    172 
    173   // Take a new snapshot of the concerned histograms before each test
    174   histogram_helper_.PrepareSnapshot(kHistogramsToSnapshot,
    175                                     arraysize(kHistogramsToSnapshot));
    176 }
    177 
    178 MockAccountReconcilor* AccountReconcilorTest::GetMockReconcilor() {
    179   if (!mock_reconcilor_) {
    180     mock_reconcilor_ =
    181         static_cast<MockAccountReconcilor*>(
    182             AccountReconcilorFactory::GetForProfile(profile()));
    183   }
    184 
    185   return mock_reconcilor_;
    186 }
    187 
    188 void AccountReconcilorTest::SimulateMergeSessionCompleted(
    189     MergeSessionHelper::Observer* observer,
    190     const std::string& account_id,
    191     const GoogleServiceAuthError& error) {
    192   observer->MergeSessionCompleted(account_id, error);
    193 }
    194 
    195 void AccountReconcilorTest::SimulateRefreshTokenFetched(
    196     AccountReconcilor* reconcilor,
    197     const std::string& account_id,
    198     const std::string& refresh_token) {
    199   reconcilor->HandleRefreshTokenFetched(account_id, refresh_token);
    200 }
    201 
    202 TEST_F(AccountReconcilorTest, Basic) {
    203   AccountReconcilor* reconcilor =
    204       AccountReconcilorFactory::GetForProfile(profile());
    205   ASSERT_TRUE(reconcilor);
    206   ASSERT_EQ(token_service(), reconcilor->token_service());
    207 }
    208 
    209 #if !defined(OS_CHROMEOS)
    210 
    211 // This method requires the use of the |TestSigninClient| to be created from the
    212 // |ChromeSigninClientFactory| because it overrides the |GoogleSigninSucceeded|
    213 // method with an empty implementation. On MacOS, the normal implementation
    214 // causes the try_bots to time out.
    215 TEST_F(AccountReconcilorTest, SigninManagerRegistration) {
    216   AccountReconcilor* reconcilor =
    217       AccountReconcilorFactory::GetForProfile(profile());
    218   ASSERT_TRUE(reconcilor);
    219   ASSERT_FALSE(reconcilor->IsRegisteredWithTokenService());
    220 
    221   signin_manager()->set_password("password");
    222   signin_manager()->OnExternalSigninCompleted(kTestEmail);
    223   ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
    224 
    225   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
    226 
    227   signin_manager()->SignOut(signin_metrics::SIGNOUT_TEST);
    228   ASSERT_FALSE(reconcilor->IsRegisteredWithTokenService());
    229 }
    230 
    231 // This method requires the use of the |TestSigninClient| to be created from the
    232 // |ChromeSigninClientFactory| because it overrides the |GoogleSigninSucceeded|
    233 // method with an empty implementation. On MacOS, the normal implementation
    234 // causes the try_bots to time out.
    235 TEST_F(AccountReconcilorTest, Reauth) {
    236   signin_manager()->SetAuthenticatedUsername(kTestEmail);
    237   signin_manager()->set_password("password");
    238 
    239   AccountReconcilor* reconcilor =
    240       AccountReconcilorFactory::GetForProfile(profile());
    241   ASSERT_TRUE(reconcilor);
    242   ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
    243 
    244   // Simulate reauth.  The state of the reconcilor should not change.
    245   signin_manager()->OnExternalSigninCompleted(kTestEmail);
    246   ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
    247 }
    248 
    249 #endif  // !defined(OS_CHROMEOS)
    250 
    251 TEST_F(AccountReconcilorTest, ProfileAlreadyConnected) {
    252   signin_manager()->SetAuthenticatedUsername(kTestEmail);
    253 
    254   AccountReconcilor* reconcilor =
    255       AccountReconcilorFactory::GetForProfile(profile());
    256   ASSERT_TRUE(reconcilor);
    257   ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
    258 }
    259 
    260 TEST_F(AccountReconcilorTest, GetAccountsFromCookieSuccess) {
    261   signin_manager()->SetAuthenticatedUsername(kTestEmail);
    262   token_service()->UpdateCredentials(kTestEmail, "refresh_token");
    263   AccountReconcilor* reconcilor =
    264       AccountReconcilorFactory::GetForProfile(profile());
    265   ASSERT_TRUE(reconcilor);
    266 
    267   SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
    268       "[\"f\", [[\"b\", 0, \"n\", \"user (at) gmail.com\", \"p\", 0, 0, 0, 0, 0]]]",
    269       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    270 
    271   reconcilor->StartReconcile();
    272   ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
    273 
    274   base::RunLoop().RunUntilIdle();
    275   ASSERT_TRUE(reconcilor->AreGaiaAccountsSet());
    276   const std::vector<std::pair<std::string, bool> >& accounts =
    277       reconcilor->GetGaiaAccountsForTesting();
    278   ASSERT_EQ(1u, accounts.size());
    279   ASSERT_EQ("user (at) gmail.com", accounts[0].first);
    280 }
    281 
    282 TEST_F(AccountReconcilorTest, GetAccountsFromCookieFailure) {
    283   signin_manager()->SetAuthenticatedUsername(kTestEmail);
    284   token_service()->UpdateCredentials(kTestEmail, "refresh_token");
    285   AccountReconcilor* reconcilor =
    286       AccountReconcilorFactory::GetForProfile(profile());
    287   ASSERT_TRUE(reconcilor);
    288 
    289   SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(), "",
    290       net::HTTP_NOT_FOUND, net::URLRequestStatus::SUCCESS);
    291 
    292   reconcilor->StartReconcile();
    293   ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
    294 
    295   base::RunLoop().RunUntilIdle();
    296   ASSERT_EQ(0u, reconcilor->GetGaiaAccountsForTesting().size());
    297 }
    298 
    299 TEST_F(AccountReconcilorTest, ValidateAccountsFromTokens) {
    300   signin_manager()->SetAuthenticatedUsername(kTestEmail);
    301   token_service()->UpdateCredentials(kTestEmail, "refresh_token");
    302 
    303   AccountReconcilor* reconcilor =
    304       AccountReconcilorFactory::GetForProfile(profile());
    305   ASSERT_TRUE(reconcilor);
    306 
    307   reconcilor->ValidateAccountsFromTokenService();
    308   ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
    309 
    310   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    311       "{\"id\":\"foo\"}", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    312   token_service()->IssueTokenForAllPendingRequests("access_token",
    313       base::Time::Now() + base::TimeDelta::FromHours(1));
    314 
    315   base::RunLoop().RunUntilIdle();
    316   ASSERT_TRUE(reconcilor->AreAllRefreshTokensChecked());
    317   ASSERT_EQ(1u, reconcilor->GetValidChromeAccountsForTesting().size());
    318   ASSERT_EQ(0u, reconcilor->GetInvalidChromeAccountsForTesting().size());
    319 }
    320 
    321 TEST_F(AccountReconcilorTest, ValidateAccountsFromTokensFailedUserInfo) {
    322   signin_manager()->SetAuthenticatedUsername(kTestEmail);
    323   token_service()->UpdateCredentials(kTestEmail, "refresh_token");
    324 
    325   AccountReconcilor* reconcilor =
    326       AccountReconcilorFactory::GetForProfile(profile());
    327   ASSERT_TRUE(reconcilor);
    328 
    329   reconcilor->ValidateAccountsFromTokenService();
    330   ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
    331 
    332   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    333       "", net::HTTP_NOT_FOUND, net::URLRequestStatus::SUCCESS);
    334   token_service()->IssueTokenForAllPendingRequests("access_token",
    335       base::Time::Now() + base::TimeDelta::FromHours(1));
    336 
    337   base::RunLoop().RunUntilIdle();
    338   ASSERT_TRUE(reconcilor->AreAllRefreshTokensChecked());
    339   ASSERT_EQ(0u, reconcilor->GetValidChromeAccountsForTesting().size());
    340   ASSERT_EQ(1u, reconcilor->GetInvalidChromeAccountsForTesting().size());
    341 }
    342 
    343 TEST_F(AccountReconcilorTest, ValidateAccountsFromTokensFailedTokenRequest) {
    344   signin_manager()->SetAuthenticatedUsername(kTestEmail);
    345   token_service()->UpdateCredentials(kTestEmail, "refresh_token");
    346 
    347   AccountReconcilor* reconcilor =
    348       AccountReconcilorFactory::GetForProfile(profile());
    349   ASSERT_TRUE(reconcilor);
    350 
    351   reconcilor->ValidateAccountsFromTokenService();
    352   ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
    353 
    354   token_service()->IssueErrorForAllPendingRequests(
    355       GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
    356 
    357   base::RunLoop().RunUntilIdle();
    358   ASSERT_TRUE(reconcilor->AreAllRefreshTokensChecked());
    359   ASSERT_EQ(0u, reconcilor->GetValidChromeAccountsForTesting().size());
    360   ASSERT_EQ(1u, reconcilor->GetInvalidChromeAccountsForTesting().size());
    361 }
    362 
    363 TEST_P(AccountReconcilorTest, StartReconcileNoop) {
    364   signin_manager()->SetAuthenticatedUsername(kTestEmail);
    365   token_service()->UpdateCredentials(kTestEmail, "refresh_token");
    366 
    367   AccountReconcilor* reconcilor =
    368       AccountReconcilorFactory::GetForProfile(profile());
    369   ASSERT_TRUE(reconcilor);
    370 
    371   SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
    372       "[\"f\", [[\"b\", 0, \"n\", \"user (at) gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
    373       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    374   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    375       "{\"id\":\"foo\"}", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    376 
    377   reconcilor->StartReconcile();
    378   ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
    379   ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
    380 
    381   base::RunLoop().RunUntilIdle();
    382   ASSERT_TRUE(reconcilor->AreGaiaAccountsSet());
    383   ASSERT_EQ(1u, reconcilor->GetGaiaAccountsForTesting().size());
    384   ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
    385 
    386   token_service()->IssueAllTokensForAccount("user (at) gmail.com", "access_token",
    387       base::Time::Now() + base::TimeDelta::FromHours(1));
    388 
    389   base::RunLoop().RunUntilIdle();
    390   ASSERT_TRUE(reconcilor->AreAllRefreshTokensChecked());
    391   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    392 
    393   histogram_helper()->Fetch();
    394   histogram_helper()->ExpectTotalCount(
    395       "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun", 1);
    396   histogram_helper()->ExpectUniqueSample(
    397       "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
    398       signin_metrics::ACCOUNTS_SAME,
    399       1);
    400 }
    401 
    402 // This is test is needed until chrome changes to use gaia obfuscated id.
    403 // The signin manager and token service use the gaia "email" property, which
    404 // preserves dots in usernames and preserves case. gaia::ParseListAccountsData()
    405 // however uses gaia "displayEmail" which does not preserve case, and then
    406 // passes the string through gaia::CanonicalizeEmail() which removes dots.  This
    407 // tests makes sure that an email like "Dot.S (at) hmail.com", as seen by the
    408 // token service, will be considered the same as "dots (at) gmail.com" as returned
    409 // by gaia::ParseListAccountsData().
    410 TEST_P(AccountReconcilorTest, StartReconcileNoopWithDots) {
    411   signin_manager()->SetAuthenticatedUsername("Dot.S (at) gmail.com");
    412   token_service()->UpdateCredentials("Dot.S (at) gmail.com", "refresh_token");
    413 
    414   AccountReconcilor* reconcilor =
    415       AccountReconcilorFactory::GetForProfile(profile());
    416   ASSERT_TRUE(reconcilor);
    417 
    418   SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
    419       "[\"f\", [[\"b\", 0, \"n\", \"dot.s (at) gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
    420       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    421   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    422       "{\"id\":\"foo\"}", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    423 
    424   reconcilor->StartReconcile();
    425   ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
    426   ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
    427 
    428   base::RunLoop().RunUntilIdle();
    429   ASSERT_TRUE(reconcilor->AreGaiaAccountsSet());
    430   ASSERT_EQ(1u, reconcilor->GetGaiaAccountsForTesting().size());
    431   ASSERT_STREQ("dots (at) gmail.com",
    432                reconcilor->GetGaiaAccountsForTesting()[0].first.c_str());
    433   ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
    434 
    435   token_service()->IssueAllTokensForAccount("Dot.S (at) gmail.com", "access_token",
    436       base::Time::Now() + base::TimeDelta::FromHours(1));
    437 
    438   base::RunLoop().RunUntilIdle();
    439   ASSERT_TRUE(reconcilor->AreAllRefreshTokensChecked());
    440   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    441 
    442   histogram_helper()->Fetch();
    443   histogram_helper()->ExpectUniqueSample(
    444       "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
    445       signin_metrics::ACCOUNTS_SAME,
    446       1);
    447 }
    448 
    449 TEST_P(AccountReconcilorTest, StartReconcileNoopMultiple) {
    450   signin_manager()->SetAuthenticatedUsername("user (at) gmail.com");
    451   token_service()->UpdateCredentials("user (at) gmail.com", "refresh_token");
    452   token_service()->UpdateCredentials("other (at) gmail.com", "refresh_token");
    453 
    454   AccountReconcilor* reconcilor =
    455       AccountReconcilorFactory::GetForProfile(profile());
    456   ASSERT_TRUE(reconcilor);
    457 
    458   SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
    459       "[\"f\", [[\"b\", 0, \"n\", \"user (at) gmail.com\", \"p\", 0, 0, 0, 0, 1], "
    460                "[\"b\", 0, \"n\", \"other (at) gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
    461       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    462   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    463       "{\"id\":\"foo\"}", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    464 
    465   reconcilor->StartReconcile();
    466   ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
    467   ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
    468 
    469   base::RunLoop().RunUntilIdle();
    470   ASSERT_TRUE(reconcilor->AreGaiaAccountsSet());
    471   ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
    472   ASSERT_EQ(2u, reconcilor->GetGaiaAccountsForTesting().size());
    473 
    474   token_service()->IssueAllTokensForAccount("other (at) gmail.com", "access_token",
    475       base::Time::Now() + base::TimeDelta::FromHours(1));
    476 
    477   base::RunLoop().RunUntilIdle();
    478   ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
    479 
    480   token_service()->IssueAllTokensForAccount("user (at) gmail.com", "access_token",
    481       base::Time::Now() + base::TimeDelta::FromHours(1));
    482 
    483   base::RunLoop().RunUntilIdle();
    484   ASSERT_TRUE(reconcilor->AreAllRefreshTokensChecked());
    485   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    486 
    487   histogram_helper()->Fetch();
    488   histogram_helper()->ExpectTotalCount(
    489       "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun", 1);
    490   histogram_helper()->ExpectUniqueSample(
    491       "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
    492       signin_metrics::ACCOUNTS_SAME,
    493       1);
    494 }
    495 
    496 TEST_P(AccountReconcilorTest, StartReconcileAddToCookie) {
    497   signin_manager()->SetAuthenticatedUsername("user (at) gmail.com");
    498   token_service()->UpdateCredentials("user (at) gmail.com", "refresh_token");
    499   token_service()->UpdateCredentials("other (at) gmail.com", "refresh_token");
    500 
    501   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("other (at) gmail.com"));
    502 
    503   SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
    504       "[\"f\", [[\"b\", 0, \"n\", \"user (at) gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
    505       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    506   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    507       "{\"id\":\"foo\"}", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    508 
    509   AccountReconcilor* reconcilor = GetMockReconcilor();
    510   reconcilor->StartReconcile();
    511   token_service()->IssueAllTokensForAccount("other (at) gmail.com", "access_token",
    512       base::Time::Now() + base::TimeDelta::FromHours(1));
    513   token_service()->IssueAllTokensForAccount("user (at) gmail.com", "access_token",
    514       base::Time::Now() + base::TimeDelta::FromHours(1));
    515 
    516   base::RunLoop().RunUntilIdle();
    517   ASSERT_TRUE(reconcilor->is_reconcile_started_);
    518   SimulateMergeSessionCompleted(reconcilor, "other (at) gmail.com",
    519                                 GoogleServiceAuthError::AuthErrorNone());
    520   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    521 
    522   histogram_helper()->Fetch();
    523   histogram_helper()->ExpectUniqueSample(
    524       "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
    525       signin_metrics::ACCOUNTS_SAME,
    526       1);
    527   histogram_helper()->ExpectUniqueSample(
    528       "Signin.Reconciler.AddedToCookieJar.FirstRun", 1, 1);
    529   histogram_helper()->ExpectUniqueSample(
    530       "Signin.Reconciler.AddedToChrome.FirstRun", 0, 1);
    531 }
    532 
    533 TEST_P(AccountReconcilorTest, StartReconcileAddToCookieTwice) {
    534   signin_manager()->SetAuthenticatedUsername("user (at) gmail.com");
    535   token_service()->UpdateCredentials("user (at) gmail.com", "refresh_token");
    536   token_service()->UpdateCredentials("other (at) gmail.com", "refresh_token");
    537 
    538   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("other (at) gmail.com"));
    539   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("third (at) gmail.com"));
    540 
    541   SetFakeResponse(
    542       GaiaUrls::GetInstance()->list_accounts_url().spec(),
    543       "[\"f\", [[\"b\", 0, \"n\", \"user (at) gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
    544       net::HTTP_OK,
    545       net::URLRequestStatus::SUCCESS);
    546   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    547                   "{\"id\":\"foo\"}",
    548                   net::HTTP_OK,
    549                   net::URLRequestStatus::SUCCESS);
    550 
    551   AccountReconcilor* reconcilor = GetMockReconcilor();
    552   reconcilor->StartReconcile();
    553   token_service()->IssueAllTokensForAccount(
    554       "other (at) gmail.com",
    555       "access_token",
    556       base::Time::Now() + base::TimeDelta::FromHours(1));
    557   token_service()->IssueAllTokensForAccount(
    558       "user (at) gmail.com",
    559       "access_token",
    560       base::Time::Now() + base::TimeDelta::FromHours(1));
    561 
    562   base::RunLoop().RunUntilIdle();
    563   ASSERT_TRUE(reconcilor->is_reconcile_started_);
    564   SimulateMergeSessionCompleted(
    565       reconcilor, "other (at) gmail.com", GoogleServiceAuthError::AuthErrorNone());
    566   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    567 
    568   histogram_helper()->Fetch();
    569   histogram_helper()->ExpectUniqueSample(
    570       "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
    571       signin_metrics::ACCOUNTS_SAME,
    572       1);
    573   histogram_helper()->ExpectUniqueSample(
    574       "Signin.Reconciler.AddedToCookieJar.FirstRun", 1, 1);
    575   histogram_helper()->ExpectUniqueSample(
    576       "Signin.Reconciler.AddedToChrome.FirstRun", 0, 1);
    577 
    578   // Do another pass after I've added a third account to the token service
    579 
    580   SetFakeResponse(
    581       GaiaUrls::GetInstance()->list_accounts_url().spec(),
    582       "[\"f\", [[\"b\", 0, \"n\", \"user (at) gmail.com\", \"p\", 0, 0, 0, 0, 1], "
    583       "[\"b\", 0, \"n\", \"other (at) gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
    584       net::HTTP_OK,
    585       net::URLRequestStatus::SUCCESS);
    586   // This will cause the reconcilor to fire.
    587   token_service()->UpdateCredentials("third (at) gmail.com", "refresh_token");
    588 
    589   token_service()->IssueAllTokensForAccount(
    590       "other (at) gmail.com",
    591       "access_token",
    592       base::Time::Now() + base::TimeDelta::FromHours(1));
    593   token_service()->IssueAllTokensForAccount(
    594       "user (at) gmail.com",
    595       "access_token",
    596       base::Time::Now() + base::TimeDelta::FromHours(1));
    597   token_service()->IssueAllTokensForAccount(
    598       "third (at) gmail.com",
    599       "access_token",
    600       base::Time::Now() + base::TimeDelta::FromHours(1));
    601 
    602   base::RunLoop().RunUntilIdle();
    603 
    604   ASSERT_TRUE(reconcilor->is_reconcile_started_);
    605   SimulateMergeSessionCompleted(
    606       reconcilor, "third (at) gmail.com", GoogleServiceAuthError::AuthErrorNone());
    607   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    608 
    609   histogram_helper()->Fetch();
    610   histogram_helper()->ExpectUniqueSample(
    611       "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
    612       signin_metrics::ACCOUNTS_SAME,
    613       1);
    614   histogram_helper()->ExpectUniqueSample(
    615       "Signin.Reconciler.AddedToCookieJar.FirstRun", 1, 1);
    616   histogram_helper()->ExpectUniqueSample(
    617       "Signin.Reconciler.AddedToChrome.FirstRun", 0, 1);
    618   histogram_helper()->ExpectUniqueSample(
    619       "Signin.Reconciler.DifferentPrimaryAccounts.SubsequentRun",
    620       signin_metrics::ACCOUNTS_SAME,
    621       1);
    622   histogram_helper()->ExpectUniqueSample(
    623       "Signin.Reconciler.AddedToCookieJar.SubsequentRun", 1, 1);
    624   histogram_helper()->ExpectUniqueSample(
    625       "Signin.Reconciler.AddedToChrome.SubsequentRun", 0, 1);
    626 }
    627 
    628 TEST_P(AccountReconcilorTest, StartReconcileAddToChrome) {
    629   signin_manager()->SetAuthenticatedUsername("user (at) gmail.com");
    630   token_service()->UpdateCredentials("user (at) gmail.com", "refresh_token");
    631 
    632   EXPECT_CALL(*GetMockReconcilor(),
    633               PerformAddToChromeAction("other (at) gmail.com", 1));
    634 
    635   SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
    636       "[\"f\", [[\"b\", 0, \"n\", \"user (at) gmail.com\", \"p\", 0, 0, 0, 0, 1], "
    637                "[\"b\", 0, \"n\", \"other (at) gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
    638       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    639   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    640       "{\"id\":\"foo\"}", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    641 
    642   AccountReconcilor* reconcilor = GetMockReconcilor();
    643   reconcilor->StartReconcile();
    644   token_service()->IssueAllTokensForAccount("user (at) gmail.com", "access_token",
    645       base::Time::Now() + base::TimeDelta::FromHours(1));
    646 
    647   base::RunLoop().RunUntilIdle();
    648   ASSERT_TRUE(reconcilor->is_reconcile_started_);
    649   SimulateRefreshTokenFetched(reconcilor, "other (at) gmail.com", "");
    650   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    651 
    652   histogram_helper()->Fetch();
    653   histogram_helper()->ExpectUniqueSample(
    654       "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
    655       signin_metrics::ACCOUNTS_SAME,
    656       1);
    657   histogram_helper()->ExpectUniqueSample(
    658       "Signin.Reconciler.AddedToCookieJar.FirstRun", 0, 1);
    659   histogram_helper()->ExpectUniqueSample(
    660       "Signin.Reconciler.AddedToChrome.FirstRun", 1, 1);
    661 }
    662 
    663 TEST_P(AccountReconcilorTest, StartReconcileBadPrimary) {
    664   signin_manager()->SetAuthenticatedUsername("user (at) gmail.com");
    665   token_service()->UpdateCredentials("user (at) gmail.com", "refresh_token");
    666   token_service()->UpdateCredentials("other (at) gmail.com", "refresh_token");
    667 
    668   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
    669   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("user (at) gmail.com"));
    670   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("other (at) gmail.com"));
    671 
    672   SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
    673       "[\"f\", [[\"b\", 0, \"n\", \"other (at) gmail.com\", \"p\", 0, 0, 0, 0, 1], "
    674                "[\"b\", 0, \"n\", \"user (at) gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
    675       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    676   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    677       "{\"id\":\"foo\"}", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    678 
    679   AccountReconcilor* reconcilor = GetMockReconcilor();
    680   reconcilor->StartReconcile();
    681   token_service()->IssueAllTokensForAccount("other (at) gmail.com", "access_token",
    682       base::Time::Now() + base::TimeDelta::FromHours(1));
    683   token_service()->IssueAllTokensForAccount("user (at) gmail.com", "access_token",
    684       base::Time::Now() + base::TimeDelta::FromHours(1));
    685 
    686   base::RunLoop().RunUntilIdle();
    687   ASSERT_TRUE(reconcilor->is_reconcile_started_);
    688   SimulateMergeSessionCompleted(reconcilor, "other (at) gmail.com",
    689                                 GoogleServiceAuthError::AuthErrorNone());
    690   ASSERT_TRUE(reconcilor->is_reconcile_started_);
    691   SimulateMergeSessionCompleted(reconcilor, "user (at) gmail.com",
    692                                 GoogleServiceAuthError::AuthErrorNone());
    693   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    694 
    695   histogram_helper()->Fetch();
    696   histogram_helper()->ExpectUniqueSample(
    697       "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
    698       signin_metrics::COOKIE_AND_TOKEN_PRIMARIES_DIFFERENT,
    699       1);
    700   histogram_helper()->ExpectUniqueSample(
    701       "Signin.Reconciler.AddedToCookieJar.FirstRun", 2, 1);
    702   histogram_helper()->ExpectUniqueSample(
    703       "Signin.Reconciler.AddedToChrome.FirstRun", 0, 1);
    704 }
    705 
    706 TEST_P(AccountReconcilorTest, StartReconcileOnlyOnce) {
    707   signin_manager()->SetAuthenticatedUsername(kTestEmail);
    708   token_service()->UpdateCredentials(kTestEmail, "refresh_token");
    709 
    710   AccountReconcilor* reconcilor =
    711       AccountReconcilorFactory::GetForProfile(profile());
    712   ASSERT_TRUE(reconcilor);
    713 
    714   SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
    715       "[\"f\", [[\"b\", 0, \"n\", \"user (at) gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
    716       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    717   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    718       "{\"id\":\"foo\"}", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    719 
    720   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    721   reconcilor->StartReconcile();
    722   ASSERT_TRUE(reconcilor->is_reconcile_started_);
    723 
    724   token_service()->IssueAllTokensForAccount("user (at) gmail.com", "access_token",
    725       base::Time::Now() + base::TimeDelta::FromHours(1));
    726 
    727   base::RunLoop().RunUntilIdle();
    728   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    729 }
    730 
    731 TEST_P(AccountReconcilorTest, StartReconcileWithSessionInfoExpiredDefault) {
    732   signin_manager()->SetAuthenticatedUsername("user (at) gmail.com");
    733   token_service()->UpdateCredentials("user (at) gmail.com", "refresh_token");
    734   token_service()->UpdateCredentials("other (at) gmail.com", "refresh_token");
    735 
    736   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("user (at) gmail.com"));
    737 
    738   SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
    739       "[\"f\", [[\"b\", 0, \"n\", \"user (at) gmail.com\", \"p\", 0, 0, 0, 0, 0],"
    740                "[\"b\", 0, \"n\", \"other (at) gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
    741       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    742   SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
    743       "{\"id\":\"foo\"}", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    744 
    745   AccountReconcilor* reconcilor =
    746       AccountReconcilorFactory::GetForProfile(profile());
    747   ASSERT_TRUE(reconcilor);
    748 
    749   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    750   reconcilor->StartReconcile();
    751   ASSERT_TRUE(reconcilor->is_reconcile_started_);
    752 
    753   token_service()->IssueAllTokensForAccount("user (at) gmail.com", "access_token",
    754       base::Time::Now() + base::TimeDelta::FromHours(1));
    755   token_service()->IssueAllTokensForAccount("other (at) gmail.com", "access_token",
    756       base::Time::Now() + base::TimeDelta::FromHours(1));
    757 
    758   base::RunLoop().RunUntilIdle();
    759   SimulateMergeSessionCompleted(reconcilor, "user (at) gmail.com",
    760                                 GoogleServiceAuthError::AuthErrorNone());
    761   ASSERT_FALSE(reconcilor->is_reconcile_started_);
    762 }
    763 
    764 INSTANTIATE_TEST_CASE_P(AccountReconcilorMaybeEnabled,
    765                         AccountReconcilorTest,
    766                         testing::Bool());
    767