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/test/histogram_tester.h"
10#include "base/time/time.h"
11#include "build/build_config.h"
12#include "chrome/browser/prefs/pref_service_syncable.h"
13#include "chrome/browser/signin/account_reconcilor_factory.h"
14#include "chrome/browser/signin/chrome_signin_client_factory.h"
15#include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
16#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
17#include "chrome/browser/signin/fake_signin_manager.h"
18#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
19#include "chrome/browser/signin/signin_manager_factory.h"
20#include "chrome/browser/signin/test_signin_client_builder.h"
21#include "chrome/test/base/testing_browser_process.h"
22#include "chrome/test/base/testing_profile.h"
23#include "chrome/test/base/testing_profile_manager.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
36namespace {
37
38const char kTestEmail[] = "user@gmail.com";
39
40class MockAccountReconcilor : public testing::StrictMock<AccountReconcilor> {
41 public:
42  static KeyedService* Build(content::BrowserContext* context);
43
44  MockAccountReconcilor(ProfileOAuth2TokenService* token_service,
45                        SigninManagerBase* signin_manager,
46                        SigninClient* client);
47  virtual ~MockAccountReconcilor() {}
48
49  virtual void StartFetchingExternalCcResult() OVERRIDE {
50    // Don't do this in tests.
51  }
52
53  MOCK_METHOD1(PerformMergeAction, void(const std::string& account_id));
54  MOCK_METHOD0(PerformLogoutAllAccountsAction, void());
55};
56
57// static
58KeyedService* MockAccountReconcilor::Build(content::BrowserContext* context) {
59  Profile* profile = Profile::FromBrowserContext(context);
60  AccountReconcilor* reconcilor = new MockAccountReconcilor(
61      ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
62      SigninManagerFactory::GetForProfile(profile),
63      ChromeSigninClientFactory::GetForProfile(profile));
64  reconcilor->Initialize(false /* start_reconcile_if_tokens_available */);
65  return reconcilor;
66}
67
68MockAccountReconcilor::MockAccountReconcilor(
69    ProfileOAuth2TokenService* token_service,
70    SigninManagerBase* signin_manager,
71    SigninClient* client)
72    : testing::StrictMock<AccountReconcilor>(token_service,
73                                             signin_manager,
74                                             client) {}
75
76}  // namespace
77
78class AccountReconcilorTest : public ::testing::TestWithParam<bool> {
79 public:
80  AccountReconcilorTest();
81  virtual void SetUp() OVERRIDE;
82
83  TestingProfile* profile() { return profile_; }
84  FakeSigninManagerForTesting* signin_manager() { return signin_manager_; }
85  FakeProfileOAuth2TokenService* token_service() { return token_service_; }
86  base::HistogramTester* histogram_tester() { return &histogram_tester_; }
87
88  void SetFakeResponse(const std::string& url,
89                       const std::string& data,
90                       net::HttpStatusCode code,
91                       net::URLRequestStatus::Status status) {
92    url_fetcher_factory_.SetFakeResponse(GURL(url), data, code, status);
93  }
94
95  MockAccountReconcilor* GetMockReconcilor();
96
97  void SimulateMergeSessionCompleted(
98      MergeSessionHelper::Observer* observer,
99      const std::string& account_id,
100      const GoogleServiceAuthError& error);
101
102 private:
103  content::TestBrowserThreadBundle bundle_;
104  TestingProfile* profile_;
105  FakeSigninManagerForTesting* signin_manager_;
106  FakeProfileOAuth2TokenService* token_service_;
107  MockAccountReconcilor* mock_reconcilor_;
108  net::FakeURLFetcherFactory url_fetcher_factory_;
109  scoped_ptr<TestingProfileManager> testing_profile_manager_;
110  base::HistogramTester histogram_tester_;
111
112  DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTest);
113};
114
115AccountReconcilorTest::AccountReconcilorTest()
116    : signin_manager_(NULL),
117      token_service_(NULL),
118      mock_reconcilor_(NULL),
119      url_fetcher_factory_(NULL) {}
120
121void AccountReconcilorTest::SetUp() {
122  // If it's a non-parameterized test, or we have a parameter of true, set flag.
123  if (!::testing::UnitTest::GetInstance()->current_test_info()->value_param() ||
124      GetParam()) {
125    CommandLine::ForCurrentProcess()->AppendSwitch(
126        switches::kEnableNewProfileManagement);
127  }
128
129  testing_profile_manager_.reset(
130      new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
131  ASSERT_TRUE(testing_profile_manager_.get()->SetUp());
132
133  TestingProfile::TestingFactories factories;
134  factories.push_back(std::make_pair(ChromeSigninClientFactory::GetInstance(),
135      signin::BuildTestSigninClient));
136  factories.push_back(std::make_pair(
137      ProfileOAuth2TokenServiceFactory::GetInstance(),
138      BuildFakeProfileOAuth2TokenService));
139  factories.push_back(std::make_pair(SigninManagerFactory::GetInstance(),
140      FakeSigninManagerBase::Build));
141  factories.push_back(std::make_pair(AccountReconcilorFactory::GetInstance(),
142      MockAccountReconcilor::Build));
143
144  profile_ = testing_profile_manager_.get()->CreateTestingProfile("name",
145                              scoped_ptr<PrefServiceSyncable>(),
146                              base::UTF8ToUTF16("name"), 0, std::string(),
147                              factories);
148
149  signin_manager_ =
150      static_cast<FakeSigninManagerForTesting*>(
151          SigninManagerFactory::GetForProfile(profile()));
152
153  token_service_ =
154      static_cast<FakeProfileOAuth2TokenService*>(
155          ProfileOAuth2TokenServiceFactory::GetForProfile(profile()));
156}
157
158MockAccountReconcilor* AccountReconcilorTest::GetMockReconcilor() {
159  if (!mock_reconcilor_) {
160    mock_reconcilor_ =
161        static_cast<MockAccountReconcilor*>(
162            AccountReconcilorFactory::GetForProfile(profile()));
163  }
164
165  return mock_reconcilor_;
166}
167
168void AccountReconcilorTest::SimulateMergeSessionCompleted(
169    MergeSessionHelper::Observer* observer,
170    const std::string& account_id,
171    const GoogleServiceAuthError& error) {
172  observer->MergeSessionCompleted(account_id, error);
173}
174
175TEST_F(AccountReconcilorTest, Basic) {
176  AccountReconcilor* reconcilor =
177      AccountReconcilorFactory::GetForProfile(profile());
178  ASSERT_TRUE(reconcilor);
179  ASSERT_EQ(token_service(), reconcilor->token_service());
180}
181
182#if !defined(OS_CHROMEOS)
183
184// This method requires the use of the |TestSigninClient| to be created from the
185// |ChromeSigninClientFactory| because it overrides the |GoogleSigninSucceeded|
186// method with an empty implementation. On MacOS, the normal implementation
187// causes the try_bots to time out.
188TEST_F(AccountReconcilorTest, SigninManagerRegistration) {
189  AccountReconcilor* reconcilor =
190      AccountReconcilorFactory::GetForProfile(profile());
191  ASSERT_TRUE(reconcilor);
192  ASSERT_FALSE(reconcilor->IsRegisteredWithTokenService());
193
194  signin_manager()->set_password("password");
195  signin_manager()->OnExternalSigninCompleted(kTestEmail);
196  ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
197
198  EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
199
200  signin_manager()->SignOut(signin_metrics::SIGNOUT_TEST);
201  ASSERT_FALSE(reconcilor->IsRegisteredWithTokenService());
202}
203
204// This method requires the use of the |TestSigninClient| to be created from the
205// |ChromeSigninClientFactory| because it overrides the |GoogleSigninSucceeded|
206// method with an empty implementation. On MacOS, the normal implementation
207// causes the try_bots to time out.
208TEST_F(AccountReconcilorTest, Reauth) {
209  signin_manager()->SetAuthenticatedUsername(kTestEmail);
210  signin_manager()->set_password("password");
211
212  AccountReconcilor* reconcilor =
213      AccountReconcilorFactory::GetForProfile(profile());
214  ASSERT_TRUE(reconcilor);
215  ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
216
217  // Simulate reauth.  The state of the reconcilor should not change.
218  signin_manager()->OnExternalSigninCompleted(kTestEmail);
219  ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
220}
221
222#endif  // !defined(OS_CHROMEOS)
223
224TEST_F(AccountReconcilorTest, ProfileAlreadyConnected) {
225  signin_manager()->SetAuthenticatedUsername(kTestEmail);
226
227  AccountReconcilor* reconcilor =
228      AccountReconcilorFactory::GetForProfile(profile());
229  ASSERT_TRUE(reconcilor);
230  ASSERT_TRUE(reconcilor->IsRegisteredWithTokenService());
231}
232
233TEST_F(AccountReconcilorTest, GetAccountsFromCookieSuccess) {
234  signin_manager()->SetAuthenticatedUsername(kTestEmail);
235  token_service()->UpdateCredentials(kTestEmail, "refresh_token");
236  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(kTestEmail));
237  AccountReconcilor* reconcilor =
238      AccountReconcilorFactory::GetForProfile(profile());
239  ASSERT_TRUE(reconcilor);
240
241  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
242      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 0]]]",
243      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
244
245  reconcilor->StartReconcile();
246  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
247
248  base::RunLoop().RunUntilIdle();
249  ASSERT_TRUE(reconcilor->AreGaiaAccountsSet());
250  const std::vector<std::pair<std::string, bool> >& accounts =
251      reconcilor->GetGaiaAccountsForTesting();
252  ASSERT_EQ(1u, accounts.size());
253  ASSERT_EQ("user@gmail.com", accounts[0].first);
254}
255
256TEST_F(AccountReconcilorTest, GetAccountsFromCookieFailure) {
257  signin_manager()->SetAuthenticatedUsername(kTestEmail);
258  token_service()->UpdateCredentials(kTestEmail, "refresh_token");
259  AccountReconcilor* reconcilor =
260      AccountReconcilorFactory::GetForProfile(profile());
261  ASSERT_TRUE(reconcilor);
262
263  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(), "",
264      net::HTTP_NOT_FOUND, net::URLRequestStatus::SUCCESS);
265
266  reconcilor->StartReconcile();
267  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
268
269  base::RunLoop().RunUntilIdle();
270  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
271}
272
273TEST_P(AccountReconcilorTest, StartReconcileNoop) {
274  signin_manager()->SetAuthenticatedUsername(kTestEmail);
275  token_service()->UpdateCredentials(kTestEmail, "refresh_token");
276
277  AccountReconcilor* reconcilor =
278      AccountReconcilorFactory::GetForProfile(profile());
279  ASSERT_TRUE(reconcilor);
280
281  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
282      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
283      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
284
285  reconcilor->StartReconcile();
286  ASSERT_TRUE(reconcilor->is_reconcile_started_);
287  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
288
289  base::RunLoop().RunUntilIdle();
290  ASSERT_FALSE(reconcilor->is_reconcile_started_);
291
292  histogram_tester()->ExpectTotalCount(
293      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun", 1);
294  histogram_tester()->ExpectUniqueSample(
295      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
296      signin_metrics::ACCOUNTS_SAME,
297      1);
298}
299
300// This is test is needed until chrome changes to use gaia obfuscated id.
301// The signin manager and token service use the gaia "email" property, which
302// preserves dots in usernames and preserves case. gaia::ParseListAccountsData()
303// however uses gaia "displayEmail" which does not preserve case, and then
304// passes the string through gaia::CanonicalizeEmail() which removes dots.  This
305// tests makes sure that an email like "Dot.S@hmail.com", as seen by the
306// token service, will be considered the same as "dots@gmail.com" as returned
307// by gaia::ParseListAccountsData().
308TEST_P(AccountReconcilorTest, StartReconcileNoopWithDots) {
309  signin_manager()->SetAuthenticatedUsername("Dot.S@gmail.com");
310  token_service()->UpdateCredentials("Dot.S@gmail.com", "refresh_token");
311
312  AccountReconcilor* reconcilor =
313      AccountReconcilorFactory::GetForProfile(profile());
314  ASSERT_TRUE(reconcilor);
315
316  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
317      "[\"f\", [[\"b\", 0, \"n\", \"dot.s@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
318      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
319
320  reconcilor->StartReconcile();
321  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
322
323  base::RunLoop().RunUntilIdle();
324  ASSERT_FALSE(reconcilor->is_reconcile_started_);
325
326  histogram_tester()->ExpectUniqueSample(
327      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
328      signin_metrics::ACCOUNTS_SAME,
329      1);
330}
331
332TEST_P(AccountReconcilorTest, StartReconcileNoopMultiple) {
333  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
334  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
335  token_service()->UpdateCredentials("other@gmail.com", "refresh_token");
336
337  AccountReconcilor* reconcilor =
338      AccountReconcilorFactory::GetForProfile(profile());
339  ASSERT_TRUE(reconcilor);
340
341  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
342      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1], "
343               "[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
344      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
345
346  reconcilor->StartReconcile();
347  ASSERT_FALSE(reconcilor->AreGaiaAccountsSet());
348  base::RunLoop().RunUntilIdle();
349  ASSERT_FALSE(reconcilor->is_reconcile_started_);
350
351  histogram_tester()->ExpectTotalCount(
352      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun", 1);
353  histogram_tester()->ExpectUniqueSample(
354      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
355      signin_metrics::ACCOUNTS_SAME,
356      1);
357}
358
359TEST_P(AccountReconcilorTest, StartReconcileAddToCookie) {
360  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
361  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
362  token_service()->UpdateCredentials("other@gmail.com", "refresh_token");
363
364  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("other@gmail.com"));
365
366  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
367      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
368      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
369
370  AccountReconcilor* reconcilor = GetMockReconcilor();
371  reconcilor->StartReconcile();
372
373  base::RunLoop().RunUntilIdle();
374  ASSERT_TRUE(reconcilor->is_reconcile_started_);
375  SimulateMergeSessionCompleted(reconcilor, "other@gmail.com",
376                                GoogleServiceAuthError::AuthErrorNone());
377  ASSERT_FALSE(reconcilor->is_reconcile_started_);
378
379  histogram_tester()->ExpectUniqueSample(
380      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
381      signin_metrics::ACCOUNTS_SAME,
382      1);
383  histogram_tester()->ExpectUniqueSample(
384      "Signin.Reconciler.AddedToCookieJar.FirstRun", 1, 1);
385  histogram_tester()->ExpectUniqueSample(
386      "Signin.Reconciler.RemovedFromCookieJar.FirstRun", 0, 1);
387}
388
389TEST_P(AccountReconcilorTest, StartReconcileRemoveFromCookie) {
390  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
391  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
392
393  EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
394  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("user@gmail.com"));
395
396  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
397      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1], "
398               "[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
399      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
400
401  AccountReconcilor* reconcilor = GetMockReconcilor();
402  reconcilor->StartReconcile();
403  ASSERT_TRUE(reconcilor->is_reconcile_started_);
404
405  base::RunLoop().RunUntilIdle();
406  SimulateMergeSessionCompleted(reconcilor, "user@gmail.com",
407                                GoogleServiceAuthError::AuthErrorNone());
408  ASSERT_FALSE(reconcilor->is_reconcile_started_);
409
410  histogram_tester()->ExpectUniqueSample(
411      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
412      signin_metrics::ACCOUNTS_SAME,
413      1);
414  histogram_tester()->ExpectUniqueSample(
415      "Signin.Reconciler.AddedToCookieJar.FirstRun", 0, 1);
416  histogram_tester()->ExpectUniqueSample(
417      "Signin.Reconciler.RemovedFromCookieJar.FirstRun", 1, 1);
418}
419
420TEST_P(AccountReconcilorTest, StartReconcileAddToCookieTwice) {
421  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
422  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
423  token_service()->UpdateCredentials("other@gmail.com", "refresh_token");
424
425  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("other@gmail.com"));
426  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("third@gmail.com"));
427
428  SetFakeResponse(
429      GaiaUrls::GetInstance()->list_accounts_url().spec(),
430      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
431      net::HTTP_OK,
432      net::URLRequestStatus::SUCCESS);
433
434  AccountReconcilor* reconcilor = GetMockReconcilor();
435  reconcilor->StartReconcile();
436
437  base::RunLoop().RunUntilIdle();
438  ASSERT_TRUE(reconcilor->is_reconcile_started_);
439  SimulateMergeSessionCompleted(
440      reconcilor, "other@gmail.com", GoogleServiceAuthError::AuthErrorNone());
441  ASSERT_FALSE(reconcilor->is_reconcile_started_);
442
443  histogram_tester()->ExpectUniqueSample(
444      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
445      signin_metrics::ACCOUNTS_SAME,
446      1);
447  histogram_tester()->ExpectUniqueSample(
448      "Signin.Reconciler.AddedToCookieJar.FirstRun", 1, 1);
449  histogram_tester()->ExpectUniqueSample(
450      "Signin.Reconciler.RemovedFromCookieJar.FirstRun", 0, 1);
451
452  // Do another pass after I've added a third account to the token service
453
454  SetFakeResponse(
455      GaiaUrls::GetInstance()->list_accounts_url().spec(),
456      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1], "
457      "[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
458      net::HTTP_OK,
459      net::URLRequestStatus::SUCCESS);
460  // This will cause the reconcilor to fire.
461  token_service()->UpdateCredentials("third@gmail.com", "refresh_token");
462
463  base::RunLoop().RunUntilIdle();
464
465  ASSERT_TRUE(reconcilor->is_reconcile_started_);
466  SimulateMergeSessionCompleted(
467      reconcilor, "third@gmail.com", GoogleServiceAuthError::AuthErrorNone());
468  ASSERT_FALSE(reconcilor->is_reconcile_started_);
469
470  histogram_tester()->ExpectUniqueSample(
471      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
472      signin_metrics::ACCOUNTS_SAME,
473      1);
474  histogram_tester()->ExpectUniqueSample(
475      "Signin.Reconciler.AddedToCookieJar.FirstRun", 1, 1);
476  histogram_tester()->ExpectUniqueSample(
477      "Signin.Reconciler.RemovedFromCookieJar.FirstRun", 0, 1);
478  histogram_tester()->ExpectUniqueSample(
479      "Signin.Reconciler.DifferentPrimaryAccounts.SubsequentRun",
480      signin_metrics::ACCOUNTS_SAME,
481      1);
482  histogram_tester()->ExpectUniqueSample(
483      "Signin.Reconciler.AddedToCookieJar.SubsequentRun", 1, 1);
484  histogram_tester()->ExpectUniqueSample(
485      "Signin.Reconciler.RemovedFromCookieJar.SubsequentRun", 0, 1);
486}
487
488TEST_P(AccountReconcilorTest, StartReconcileBadPrimary) {
489  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
490  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
491  token_service()->UpdateCredentials("other@gmail.com", "refresh_token");
492
493  EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
494  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("user@gmail.com"));
495  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("other@gmail.com"));
496
497  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
498      "[\"f\", [[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0, 0, 1], "
499               "[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
500      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
501
502  AccountReconcilor* reconcilor = GetMockReconcilor();
503  reconcilor->StartReconcile();
504
505  base::RunLoop().RunUntilIdle();
506  ASSERT_TRUE(reconcilor->is_reconcile_started_);
507  SimulateMergeSessionCompleted(reconcilor, "other@gmail.com",
508                                GoogleServiceAuthError::AuthErrorNone());
509  ASSERT_TRUE(reconcilor->is_reconcile_started_);
510  SimulateMergeSessionCompleted(reconcilor, "user@gmail.com",
511                                GoogleServiceAuthError::AuthErrorNone());
512  ASSERT_FALSE(reconcilor->is_reconcile_started_);
513
514  histogram_tester()->ExpectUniqueSample(
515      "Signin.Reconciler.DifferentPrimaryAccounts.FirstRun",
516      signin_metrics::COOKIE_AND_TOKEN_PRIMARIES_DIFFERENT,
517      1);
518  histogram_tester()->ExpectUniqueSample(
519      "Signin.Reconciler.AddedToCookieJar.FirstRun", 0, 1);
520  histogram_tester()->ExpectUniqueSample(
521      "Signin.Reconciler.RemovedFromCookieJar.FirstRun", 0, 1);
522}
523
524TEST_P(AccountReconcilorTest, StartReconcileOnlyOnce) {
525  signin_manager()->SetAuthenticatedUsername(kTestEmail);
526  token_service()->UpdateCredentials(kTestEmail, "refresh_token");
527
528  AccountReconcilor* reconcilor =
529      AccountReconcilorFactory::GetForProfile(profile());
530  ASSERT_TRUE(reconcilor);
531
532  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
533      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
534      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
535
536  ASSERT_FALSE(reconcilor->is_reconcile_started_);
537  reconcilor->StartReconcile();
538  ASSERT_TRUE(reconcilor->is_reconcile_started_);
539
540  base::RunLoop().RunUntilIdle();
541  ASSERT_FALSE(reconcilor->is_reconcile_started_);
542}
543
544TEST_P(AccountReconcilorTest, StartReconcileWithSessionInfoExpiredDefault) {
545  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
546  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
547  token_service()->UpdateCredentials("other@gmail.com", "refresh_token");
548
549  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("user@gmail.com"));
550
551  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
552      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 0],"
553               "[\"b\", 0, \"n\", \"other@gmail.com\", \"p\", 0, 0, 0, 0, 1]]]",
554      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
555
556  AccountReconcilor* reconcilor =
557      AccountReconcilorFactory::GetForProfile(profile());
558  ASSERT_TRUE(reconcilor);
559
560  ASSERT_FALSE(reconcilor->is_reconcile_started_);
561  reconcilor->StartReconcile();
562  ASSERT_TRUE(reconcilor->is_reconcile_started_);
563
564  base::RunLoop().RunUntilIdle();
565  SimulateMergeSessionCompleted(reconcilor, "user@gmail.com",
566                                GoogleServiceAuthError::AuthErrorNone());
567  ASSERT_FALSE(reconcilor->is_reconcile_started_);
568}
569
570TEST_F(AccountReconcilorTest, MergeSessionCompletedWithBogusAccount) {
571  signin_manager()->SetAuthenticatedUsername("user@gmail.com");
572  token_service()->UpdateCredentials("user@gmail.com", "refresh_token");
573
574  EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction("user@gmail.com"));
575
576  SetFakeResponse(GaiaUrls::GetInstance()->list_accounts_url().spec(),
577      "[\"f\", [[\"b\", 0, \"n\", \"user@gmail.com\", \"p\", 0, 0, 0, 0, 0]]]",
578      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
579
580  AccountReconcilor* reconcilor =
581      AccountReconcilorFactory::GetForProfile(profile());
582  ASSERT_TRUE(reconcilor);
583
584  ASSERT_FALSE(reconcilor->is_reconcile_started_);
585  reconcilor->StartReconcile();
586  ASSERT_TRUE(reconcilor->is_reconcile_started_);
587
588  base::RunLoop().RunUntilIdle();
589
590  // If an unknown account id is sent, it should not upset the state.
591  SimulateMergeSessionCompleted(reconcilor, "bogus@gmail.com",
592                                GoogleServiceAuthError::AuthErrorNone());
593  ASSERT_TRUE(reconcilor->is_reconcile_started_);
594
595  SimulateMergeSessionCompleted(reconcilor, "user@gmail.com",
596                                GoogleServiceAuthError::AuthErrorNone());
597  ASSERT_FALSE(reconcilor->is_reconcile_started_);
598}
599
600INSTANTIATE_TEST_CASE_P(AccountReconcilorMaybeEnabled,
601                        AccountReconcilorTest,
602                        testing::Bool());
603
604