Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / signin / account_reconcilor_unittest.cc
index bc2b14b..0bb444c 100644 (file)
@@ -2,19 +2,33 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/histogram_tester.h"
 #include "base/time/time.h"
-#include "chrome/browser/signin/account_reconcilor.h"
+#include "build/build_config.h"
+#include "chrome/browser/prefs/pref_service_syncable.h"
 #include "chrome/browser/signin/account_reconcilor_factory.h"
+#include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
+#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
 #include "chrome/browser/signin/fake_signin_manager.h"
-#include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/test_signin_client_builder.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/signin/core/browser/account_reconcilor.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/browser/signin_metrics.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "components/signin/core/common/signin_switches.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "google_apis/gaia/gaia_urls.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -23,15 +37,53 @@ namespace {
 
 const char kTestEmail[] = "user@gmail.com";
 
-class AccountReconcilorTest : public testing::Test {
+class MockAccountReconcilor : public testing::StrictMock<AccountReconcilor> {
+ public:
+  static KeyedService* Build(content::BrowserContext* context);
+
+  MockAccountReconcilor(ProfileOAuth2TokenService* token_service,
+                        SigninManagerBase* signin_manager,
+                        SigninClient* client);
+  virtual ~MockAccountReconcilor() {}
+
+  virtual void StartFetchingExternalCcResult() OVERRIDE {
+    // Don't do this in tests.
+  }
+
+  MOCK_METHOD1(PerformMergeAction, void(const std::string& account_id));
+  MOCK_METHOD0(PerformLogoutAllAccountsAction, void());
+};
+
+// static
+KeyedService* MockAccountReconcilor::Build(content::BrowserContext* context) {
+  Profile* profile = Profile::FromBrowserContext(context);
+  AccountReconcilor* reconcilor = new MockAccountReconcilor(
+      ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
+      SigninManagerFactory::GetForProfile(profile),
+      ChromeSigninClientFactory::GetForProfile(profile));
+  reconcilor->Initialize(false /* start_reconcile_if_tokens_available */);
+  return reconcilor;
+}
+
+MockAccountReconcilor::MockAccountReconcilor(
+    ProfileOAuth2TokenService* token_service,
+    SigninManagerBase* signin_manager,
+    SigninClient* client)
+    : testing::StrictMock<AccountReconcilor>(token_service,
+                                             signin_manager,
+                                             client) {}
+
+}  // namespace
+
+class AccountReconcilorTest : public ::testing::TestWithParam<bool> {
  public:
   AccountReconcilorTest();
   virtual void SetUp() OVERRIDE;
-  virtual void TearDown() OVERRIDE;
 
-  TestingProfile* profile() { return profile_.get(); }
+  TestingProfile* profile() { return profile_; }
   FakeSigninManagerForTesting* signin_manager() { return signin_manager_; }
   FakeProfileOAuth2TokenService* token_service() { return token_service_; }
+  base::HistogramTester* histogram_tester() { return &histogram_tester_; }
 
   void SetFakeResponse(const std::string& url,
                        const std::string& data,
@@ -40,81 +92,130 @@ class AccountReconcilorTest : public testing::Test {
     url_fetcher_factory_.SetFakeResponse(GURL(url), data, code, status);
   }
 
-private:
+  MockAccountReconcilor* GetMockReconcilor();
+
+  void SimulateMergeSessionCompleted(
+      MergeSessionHelper::Observer* observer,
+      const std::string& account_id,
+      const GoogleServiceAuthError& error);
+
+ private:
   content::TestBrowserThreadBundle bundle_;
-  scoped_ptr<TestingProfile> profile_;
+  TestingProfile* profile_;
   FakeSigninManagerForTesting* signin_manager_;
   FakeProfileOAuth2TokenService* token_service_;
+  MockAccountReconcilor* mock_reconcilor_;
   net::FakeURLFetcherFactory url_fetcher_factory_;
+  scoped_ptr<TestingProfileManager> testing_profile_manager_;
+  base::HistogramTester histogram_tester_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTest);
 };
 
 AccountReconcilorTest::AccountReconcilorTest()
     : signin_manager_(NULL),
       token_service_(NULL),
+      mock_reconcilor_(NULL),
       url_fetcher_factory_(NULL) {}
 
 void AccountReconcilorTest::SetUp() {
-  TestingProfile::Builder builder;
-  builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(),
-                            FakeProfileOAuth2TokenService::Build);
-  builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
-                            FakeSigninManagerBase::Build);
-  profile_ = builder.Build();
+  // If it's a non-parameterized test, or we have a parameter of true, set flag.
+  if (!::testing::UnitTest::GetInstance()->current_test_info()->value_param() ||
+      GetParam()) {
+    CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kEnableNewProfileManagement);
+  }
+
+  testing_profile_manager_.reset(
+      new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
+  ASSERT_TRUE(testing_profile_manager_.get()->SetUp());
+
+  TestingProfile::TestingFactories factories;
+  factories.push_back(std::make_pair(ChromeSigninClientFactory::GetInstance(),
+      signin::BuildTestSigninClient));
+  factories.push_back(std::make_pair(
+      ProfileOAuth2TokenServiceFactory::GetInstance(),
+      BuildFakeProfileOAuth2TokenService));
+  factories.push_back(std::make_pair(SigninManagerFactory::GetInstance(),
+      FakeSigninManagerBase::Build));
+  factories.push_back(std::make_pair(AccountReconcilorFactory::GetInstance(),
+      MockAccountReconcilor::Build));
+
+  profile_ = testing_profile_manager_.get()->CreateTestingProfile("name",
+                              scoped_ptr<PrefServiceSyncable>(),
+                              base::UTF8ToUTF16("name"), 0, std::string(),
+                              factories);
 
   signin_manager_ =
       static_cast<FakeSigninManagerForTesting*>(
           SigninManagerFactory::GetForProfile(profile()));
-  signin_manager_->Initialize(profile(), NULL);
 
   token_service_ =
       static_cast<FakeProfileOAuth2TokenService*>(
           ProfileOAuth2TokenServiceFactory::GetForProfile(profile()));
 }
 
-void AccountReconcilorTest::TearDown() {
-  // Destroy the profile before all threads are torn down.
-  profile_.reset();
+MockAccountReconcilor* AccountReconcilorTest::GetMockReconcilor() {
+  if (!mock_reconcilor_) {
+    mock_reconcilor_ =
+        static_cast<MockAccountReconcilor*>(
+            AccountReconcilorFactory::GetForProfile(profile()));
+  }
+
+  return mock_reconcilor_;
 }
 
-}  // namespace
+void AccountReconcilorTest::SimulateMergeSessionCompleted(
+    MergeSessionHelper::Observer* observer,
+    const std::string& account_id,
+    const GoogleServiceAuthError& error) {
+  observer->MergeSessionCompleted(account_id, error);
+}
 
 TEST_F(AccountReconcilorTest, Basic) {
   AccountReconcilor* reconcilor =
       AccountReconcilorFactory::GetForProfile(profile());
   ASSERT_TRUE(reconcilor);
-  ASSERT_EQ(profile(), reconcilor->profile());
+  ASSERT_EQ(token_service(), reconcilor->token_service());
 }
 
 #if !defined(OS_CHROMEOS)
 
+// This method requires the use of the |TestSigninClient| to be created from the
+// |ChromeSigninClientFactory| because it overrides the |GoogleSigninSucceeded|
+// method with an empty implementation. On MacOS, the normal implementation
+// causes the try_bots to time out.
 TEST_F(AccountReconcilorTest, SigninManagerRegistration) {
   AccountReconcilor* reconcilor =
       AccountReconcilorFactory::GetForProfile(profile());
   ASSERT_TRUE(reconcilor);
-  ASSERT_FALSE(reconcilor->IsPeriodicReconciliationRunning());
   ASSERT_FALSE(reconcilor->IsRegisteredWithTokenService());
 
+  signin_manager()->set_password("password");
   signin_manager()->OnExternalSigninCompleted(kTestEmail);
-  ASSERT_TRUE(reconcilor->IsPeriodicReconciliationRunning());
   ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
 
-  signin_manager()->SignOut();
-  ASSERT_FALSE(reconcilor->IsPeriodicReconciliationRunning());
+  EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
+
+  signin_manager()->SignOut(signin_metrics::SIGNOUT_TEST);
   ASSERT_FALSE(reconcilor->IsRegisteredWithTokenService());
 }
 
+// This method requires the use of the |TestSigninClient| to be created from the
+// |ChromeSigninClientFactory| because it overrides the |GoogleSigninSucceeded|
+// method with an empty implementation. On MacOS, the normal implementation
+// causes the try_bots to time out.
 TEST_F(AccountReconcilorTest, Reauth) {
   signin_manager()->SetAuthenticatedUsername(kTestEmail);
+  signin_manager()->set_password("password");
 
   AccountReconcilor* reconcilor =
       AccountReconcilorFactory::GetForProfile(profile());
   ASSERT_TRUE(reconcilor);
-  ASSERT_TRUE(reconcilor->IsPeriodicReconciliationRunning());
   ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
 
   // Simulate reauth.  The state of the reconcilor should not change.
   signin_manager()->OnExternalSigninCompleted(kTestEmail);
-  ASSERT_TRUE(reconcilor->IsPeriodicReconciliationRunning());
   ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
 }
 
@@ -126,52 +227,50 @@ TEST_F(AccountReconcilorTest, ProfileAlreadyConnected) {
   AccountReconcilor* reconcilor =
       AccountReconcilorFactory::GetForProfile(profile());
   ASSERT_TRUE(reconcilor);
-  ASSERT_TRUE(reconcilor->IsPeriodicReconciliationRunning());
   ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
 }
 
 TEST_F(AccountReconcilorTest, GetAccountsFromCookieSuccess) {
   signin_manager()->SetAuthenticatedUsername(kTestEmail);
+  token_service()->UpdateCredentials(kTestEmail, "refresh_token");
+  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(kTestEmail));
   AccountReconcilor* reconcilor =
       AccountReconcilorFactory::GetForProfile(profile());
   ASSERT_TRUE(reconcilor);
 
-  SetFakeResponse("https://accounts.google.com/ListAccounts",
-      "[\"foo\", [[\"bar\", 0, \"name\", \"email\", \"photo\", 0, 0, 0]]]",
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 0]]]",
       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
 
-  reconcilor->GetAccountsFromCookie(base::Bind(
-      &AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts,
-          base::Unretained(reconcilor)));
+  reconcilor->StartReconcile();
   ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
 
   base::RunLoop().RunUntilIdle();
   ASSERT_TRUE(reconcilor->AreGaiaAccountsSet());
-  const std::vector<std::string>& accounts =
+  const std::vector<std::pair<std::string, bool> >& accounts =
       reconcilor->GetGaiaAccountsForTesting();
   ASSERT_EQ(1u, accounts.size());
-  ASSERT_EQ("email", accounts[0]);
+  ASSERT_EQ("user@gmail.com", accounts[0].first);
 }
 
 TEST_F(AccountReconcilorTest, GetAccountsFromCookieFailure) {
   signin_manager()->SetAuthenticatedUsername(kTestEmail);
+  token_service()->UpdateCredentials(kTestEmail, "refresh_token");
   AccountReconcilor* reconcilor =
       AccountReconcilorFactory::GetForProfile(profile());
   ASSERT_TRUE(reconcilor);
 
-  SetFakeResponse("https://accounts.google.com/ListAccounts", "",
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(), "",
       net::HTTP_NOT_FOUND, net::URLRequestStatus::SUCCESS);
 
-  reconcilor->GetAccountsFromCookie(base::Bind(
-      &AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts,
-          base::Unretained(reconcilor)));
+  reconcilor->StartReconcile();
   ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
 
   base::RunLoop().RunUntilIdle();
-  ASSERT_EQ(0u, reconcilor->GetGaiaAccountsForTesting().size());
+  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
 }
 
-TEST_F(AccountReconcilorTest, ValidateAccountsFromTokens) {
+TEST_P(AccountReconcilorTest, StartReconcileNoop) {
   signin_manager()->SetAuthenticatedUsername(kTestEmail);
   token_service()->UpdateCredentials(kTestEmail, "refresh_token");
 
@@ -179,63 +278,250 @@ TEST_F(AccountReconcilorTest, ValidateAccountsFromTokens) {
       AccountReconcilorFactory::GetForProfile(profile());
   ASSERT_TRUE(reconcilor);
 
-  reconcilor->ValidateAccountsFromTokenService();
-  ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
+      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
 
-  SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
-      "{\"id\":\"foo\"}", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
-  token_service()->IssueTokenForAllPendingRequests("access_token",
-      base::Time::Now() + base::TimeDelta::FromHours(1));
+  reconcilor->StartReconcile();
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
+  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
 
   base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(reconcilor->AreAllRefreshTokensChecked());
-  ASSERT_EQ(1u, reconcilor->GetValidChromeAccountsForTesting().size());
-  ASSERT_EQ(0u, reconcilor->GetInvalidChromeAccountsForTesting().size());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+
+  histogram_tester()->ExpectTotalCount(
+      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun", 1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
+      signin_metrics::ACCOUNTS_SAME,
+      1);
 }
 
-TEST_F(AccountReconcilorTest, ValidateAccountsFromTokensFailedUserInfo) {
-  signin_manager()->SetAuthenticatedUsername(kTestEmail);
-  token_service()->UpdateCredentials(kTestEmail, "refresh_token");
+// This is test is needed until chrome changes to use gaia obfuscated id.
+// The signin manager and token service use the gaia "email" property, which
+// preserves dots in usernames and preserves case. gaia::ParseListAccountsData()
+// however uses gaia "displayEmail" which does not preserve case, and then
+// passes the string through gaia::CanonicalizeEmail() which removes dots.  This
+// tests makes sure that an email like "Dot.S@hmail.com", as seen by the
+// token service, will be considered the same as "dots@gmail.com" as returned
+// by gaia::ParseListAccountsData().
+TEST_P(AccountReconcilorTest, StartReconcileNoopWithDots) {
+  signin_manager()->SetAuthenticatedUsername("Dot.S@gmail.com");
+  token_service()->UpdateCredentials("Dot.S@gmail.com", "refresh_token");
 
   AccountReconcilor* reconcilor =
       AccountReconcilorFactory::GetForProfile(profile());
   ASSERT_TRUE(reconcilor);
 
-  reconcilor->ValidateAccountsFromTokenService();
-  ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"dot.s@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
+      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
 
-  SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
-      "", net::HTTP_NOT_FOUND, net::URLRequestStatus::SUCCESS);
-  token_service()->IssueTokenForAllPendingRequests("access_token",
-      base::Time::Now() + base::TimeDelta::FromHours(1));
+  reconcilor->StartReconcile();
+  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
 
   base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(reconcilor->AreAllRefreshTokensChecked());
-  ASSERT_EQ(0u, reconcilor->GetValidChromeAccountsForTesting().size());
-  ASSERT_EQ(1u, reconcilor->GetInvalidChromeAccountsForTesting().size());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
+      signin_metrics::ACCOUNTS_SAME,
+      1);
 }
 
-TEST_F(AccountReconcilorTest, ValidateAccountsFromTokensFailedTokenRequest) {
-  signin_manager()->SetAuthenticatedUsername(kTestEmail);
-  token_service()->UpdateCredentials(kTestEmail, "refresh_token");
+TEST_P(AccountReconcilorTest, StartReconcileNoopMultiple) {
+  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
+  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
+  token_service()->UpdateCredentials("other@gmail.com", "refresh_token");
 
   AccountReconcilor* reconcilor =
       AccountReconcilorFactory::GetForProfile(profile());
   ASSERT_TRUE(reconcilor);
 
-  reconcilor->ValidateAccountsFromTokenService();
-  ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1], "
+               "[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
+      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
 
-  token_service()->IssueErrorForAllPendingRequests(
-      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  reconcilor->StartReconcile();
+  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+
+  histogram_tester()->ExpectTotalCount(
+      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun", 1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
+      signin_metrics::ACCOUNTS_SAME,
+      1);
+}
+
+TEST_P(AccountReconcilorTest, StartReconcileAddToCookie) {
+  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
+  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
+  token_service()->UpdateCredentials("other@gmail.com", "refresh_token");
+
+  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("other@gmail.com"));
+
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
+      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+
+  AccountReconcilor* reconcilor = GetMockReconcilor();
+  reconcilor->StartReconcile();
 
   base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(reconcilor->AreAllRefreshTokensChecked());
-  ASSERT_EQ(0u, reconcilor->GetValidChromeAccountsForTesting().size());
-  ASSERT_EQ(1u, reconcilor->GetInvalidChromeAccountsForTesting().size());
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
+  SimulateMergeSessionCompleted(reconcilor, "other@gmail.com",
+                                GoogleServiceAuthError::AuthErrorNone());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
+      signin_metrics::ACCOUNTS_SAME,
+      1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.AddedToCookieJar.FirstRun", 1, 1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.RemovedFromCookieJar.FirstRun", 0, 1);
 }
 
-TEST_F(AccountReconcilorTest, StartReconcileAction) {
+TEST_P(AccountReconcilorTest, StartReconcileRemoveFromCookie) {
+  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
+  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
+
+  EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
+  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("user@gmail.com"));
+
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1], "
+               "[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
+      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+
+  AccountReconcilor* reconcilor = GetMockReconcilor();
+  reconcilor->StartReconcile();
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
+
+  base::RunLoop().RunUntilIdle();
+  SimulateMergeSessionCompleted(reconcilor, "user@gmail.com",
+                                GoogleServiceAuthError::AuthErrorNone());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
+      signin_metrics::ACCOUNTS_SAME,
+      1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.AddedToCookieJar.FirstRun", 0, 1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.RemovedFromCookieJar.FirstRun", 1, 1);
+}
+
+TEST_P(AccountReconcilorTest, StartReconcileAddToCookieTwice) {
+  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
+  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
+  token_service()->UpdateCredentials("other@gmail.com", "refresh_token");
+
+  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("other@gmail.com"));
+  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("third@gmail.com"));
+
+  SetFakeResponse(
+      GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
+      net::HTTP_OK,
+      net::URLRequestStatus::SUCCESS);
+
+  AccountReconcilor* reconcilor = GetMockReconcilor();
+  reconcilor->StartReconcile();
+
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
+  SimulateMergeSessionCompleted(
+      reconcilor, "other@gmail.com", GoogleServiceAuthError::AuthErrorNone());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
+      signin_metrics::ACCOUNTS_SAME,
+      1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.AddedToCookieJar.FirstRun", 1, 1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.RemovedFromCookieJar.FirstRun", 0, 1);
+
+  // Do another pass after I've added a third account to the token service
+
+  SetFakeResponse(
+      GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1], "
+      "[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
+      net::HTTP_OK,
+      net::URLRequestStatus::SUCCESS);
+  // This will cause the reconcilor to fire.
+  token_service()->UpdateCredentials("third@gmail.com", "refresh_token");
+
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
+  SimulateMergeSessionCompleted(
+      reconcilor, "third@gmail.com", GoogleServiceAuthError::AuthErrorNone());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
+      signin_metrics::ACCOUNTS_SAME,
+      1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.AddedToCookieJar.FirstRun", 1, 1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.RemovedFromCookieJar.FirstRun", 0, 1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.DifferentPrimaryAccounts.SubsequentRun",
+      signin_metrics::ACCOUNTS_SAME,
+      1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.AddedToCookieJar.SubsequentRun", 1, 1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.RemovedFromCookieJar.SubsequentRun", 0, 1);
+}
+
+TEST_P(AccountReconcilorTest, StartReconcileBadPrimary) {
+  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
+  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
+  token_service()->UpdateCredentials("other@gmail.com", "refresh_token");
+
+  EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
+  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("user@gmail.com"));
+  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("other@gmail.com"));
+
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0, 0, 1], "
+               "[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
+      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+
+  AccountReconcilor* reconcilor = GetMockReconcilor();
+  reconcilor->StartReconcile();
+
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
+  SimulateMergeSessionCompleted(reconcilor, "other@gmail.com",
+                                GoogleServiceAuthError::AuthErrorNone());
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
+  SimulateMergeSessionCompleted(reconcilor, "user@gmail.com",
+                                GoogleServiceAuthError::AuthErrorNone());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
+      signin_metrics::COOKIE_AND_TOKEN_PRIMARIES_DIFFERENT,
+      1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.AddedToCookieJar.FirstRun", 0, 1);
+  histogram_tester()->ExpectUniqueSample(
+      "Signin.Reconciler.RemovedFromCookieJar.FirstRun", 0, 1);
+}
+
+TEST_P(AccountReconcilorTest, StartReconcileOnlyOnce) {
   signin_manager()->SetAuthenticatedUsername(kTestEmail);
   token_service()->UpdateCredentials(kTestEmail, "refresh_token");
 
@@ -243,31 +529,75 @@ TEST_F(AccountReconcilorTest, StartReconcileAction) {
       AccountReconcilorFactory::GetForProfile(profile());
   ASSERT_TRUE(reconcilor);
 
-  SetFakeResponse("https://accounts.google.com/ListAccounts",
-      "[\"foo\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0], "
-                 "[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0]]]",
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
       net::HTTP_OK, net::URLRequestStatus::SUCCESS);
 
-  reconcilor->StartReconcileAction();
-  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
-  ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+  reconcilor->StartReconcile();
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
 
   base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(reconcilor->AreGaiaAccountsSet());
-  ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
-  ASSERT_EQ(2u, reconcilor->GetGaiaAccountsForTesting().size());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+}
+
+TEST_P(AccountReconcilorTest, StartReconcileWithSessionInfoExpiredDefault) {
+  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
+  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
+  token_service()->UpdateCredentials("other@gmail.com", "refresh_token");
+
+  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("user@gmail.com"));
+
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 0],"
+               "[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
+      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
 
-  SetFakeResponse("https://www.googleapis.com/oauth2/v1/userinfo",
-      "", net::HTTP_NOT_FOUND, net::URLRequestStatus::SUCCESS);
-  token_service()->IssueAllTokensForAccount("other@gmail.com", "access_token",
-      base::Time::Now() + base::TimeDelta::FromHours(1));
+  AccountReconcilor* reconcilor =
+      AccountReconcilorFactory::GetForProfile(profile());
+  ASSERT_TRUE(reconcilor);
+
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+  reconcilor->StartReconcile();
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
 
   base::RunLoop().RunUntilIdle();
-  ASSERT_FALSE(reconcilor->AreAllRefreshTokensChecked());
+  SimulateMergeSessionCompleted(reconcilor, "user@gmail.com",
+                                GoogleServiceAuthError::AuthErrorNone());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+}
+
+TEST_F(AccountReconcilorTest, MergeSessionCompletedWithBogusAccount) {
+  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
+  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
 
-  token_service()->IssueAllTokensForAccount("user@gmail.com", "access_token",
-      base::Time::Now() + base::TimeDelta::FromHours(1));
+  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("user@gmail.com"));
+
+  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
+      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 0]]]",
+      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+
+  AccountReconcilor* reconcilor =
+      AccountReconcilorFactory::GetForProfile(profile());
+  ASSERT_TRUE(reconcilor);
+
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
+  reconcilor->StartReconcile();
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
 
   base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(reconcilor->AreAllRefreshTokensChecked());
+
+  // If an unknown account id is sent, it should not upset the state.
+  SimulateMergeSessionCompleted(reconcilor, "bogus@gmail.com",
+                                GoogleServiceAuthError::AuthErrorNone());
+  ASSERT_TRUE(reconcilor->is_reconcile_started_);
+
+  SimulateMergeSessionCompleted(reconcilor, "user@gmail.com",
+                                GoogleServiceAuthError::AuthErrorNone());
+  ASSERT_FALSE(reconcilor->is_reconcile_started_);
 }
+
+INSTANTIATE_TEST_CASE_P(AccountReconcilorMaybeEnabled,
+                        AccountReconcilorTest,
+                        testing::Bool());
+