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 <algorithm>
6 #include <cstring>
7 #include <map>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 
12 #include "base/macros.h"
13 #include "base/run_loop.h"
14 #include "base/scoped_observer.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/test/metrics/histogram_tester.h"
17 #include "base/test/scoped_feature_list.h"
18 #include "base/test/task_environment.h"
19 #include "base/time/time.h"
20 #include "base/timer/mock_timer.h"
21 #include "build/build_config.h"
22 #include "components/prefs/pref_service.h"
23 #include "components/signin/core/browser/account_reconcilor.h"
24 #include "components/signin/core/browser/mirror_account_reconcilor_delegate.h"
25 #include "components/signin/public/base/account_consistency_method.h"
26 #include "components/signin/public/base/list_accounts_test_utils.h"
27 #include "components/signin/public/base/signin_buildflags.h"
28 #include "components/signin/public/base/signin_metrics.h"
29 #include "components/signin/public/base/signin_pref_names.h"
30 #include "components/signin/public/base/test_signin_client.h"
31 #include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
32 #include "components/signin/public/identity_manager/identity_test_environment.h"
33 #include "components/signin/public/identity_manager/identity_test_utils.h"
34 #include "components/signin/public/identity_manager/primary_account_mutator.h"
35 #include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h"
36 #include "components/sync_preferences/testing_pref_service_syncable.h"
37 #include "google_apis/gaia/gaia_constants.h"
38 #include "google_apis/gaia/gaia_urls.h"
39 #include "google_apis/gaia/google_service_auth_error.h"
40 #include "testing/gmock/include/gmock/gmock.h"
41 #include "testing/gtest/include/gtest/gtest.h"
42 
43 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
44 #include "components/signin/core/browser/dice_account_reconcilor_delegate.h"
45 #endif
46 
47 #if defined(OS_CHROMEOS)
48 #include "chromeos/tpm/stub_install_attributes.h"
49 #include "components/signin/core/browser/active_directory_account_reconcilor_delegate.h"
50 #endif
51 
52 using signin::RevokeTokenAction;
53 using signin_metrics::AccountReconcilorState;
54 
55 namespace {
56 
57 // An AccountReconcilorDelegate that records all calls (Spy pattern).
58 class SpyReconcilorDelegate : public signin::AccountReconcilorDelegate {
59  public:
60   int num_reconcile_finished_calls_{0};
61   int num_reconcile_timeout_calls_{0};
62 
IsReconcileEnabled() const63   bool IsReconcileEnabled() const override { return true; }
64 
IsAccountConsistencyEnforced() const65   bool IsAccountConsistencyEnforced() const override { return true; }
66 
GetGaiaApiSource() const67   gaia::GaiaSource GetGaiaApiSource() const override {
68     return gaia::GaiaSource::kChrome;
69   }
70 
ShouldAbortReconcileIfPrimaryHasError() const71   bool ShouldAbortReconcileIfPrimaryHasError() const override { return true; }
72 
GetFirstGaiaAccountForReconcile(const std::vector<CoreAccountId> & chrome_accounts,const std::vector<gaia::ListedAccount> & gaia_accounts,const CoreAccountId & primary_account,bool first_execution,bool will_logout) const73   CoreAccountId GetFirstGaiaAccountForReconcile(
74       const std::vector<CoreAccountId>& chrome_accounts,
75       const std::vector<gaia::ListedAccount>& gaia_accounts,
76       const CoreAccountId& primary_account,
77       bool first_execution,
78       bool will_logout) const override {
79     return primary_account;
80   }
81 
GetChromeAccountsForReconcile(const std::vector<CoreAccountId> & chrome_accounts,const CoreAccountId & primary_account,const std::vector<gaia::ListedAccount> & gaia_accounts,const gaia::MultiloginMode mode) const82   std::vector<CoreAccountId> GetChromeAccountsForReconcile(
83       const std::vector<CoreAccountId>& chrome_accounts,
84       const CoreAccountId& primary_account,
85       const std::vector<gaia::ListedAccount>& gaia_accounts,
86       const gaia::MultiloginMode mode) const override {
87     return chrome_accounts;
88   }
89 
OnReconcileFinished(const CoreAccountId & first_account)90   void OnReconcileFinished(const CoreAccountId& first_account) override {
91     ++num_reconcile_finished_calls_;
92   }
93 
GetReconcileTimeout() const94   base::TimeDelta GetReconcileTimeout() const override {
95     // Does not matter as long as it is different from base::TimeDelta::Max().
96     return base::TimeDelta::FromMinutes(100);
97   }
98 
OnReconcileError(const GoogleServiceAuthError & error)99   void OnReconcileError(const GoogleServiceAuthError& error) override {
100     ++num_reconcile_timeout_calls_;
101   }
102 };
103 
104 // gmock does not allow mocking classes with move-only parameters, preventing
105 // from mocking the AccountReconcilor class directly (because of the
106 // unique_ptr<AccountReconcilorDelegate> parameter).
107 // Introduce a dummy class creating the delegate internally, to avoid the move.
108 class DummyAccountReconcilorWithDelegate : public AccountReconcilor {
109  public:
DummyAccountReconcilorWithDelegate(signin::IdentityManager * identity_manager,SigninClient * client,signin::AccountConsistencyMethod account_consistency,bool dice_migration_completed)110   DummyAccountReconcilorWithDelegate(
111       signin::IdentityManager* identity_manager,
112       SigninClient* client,
113       signin::AccountConsistencyMethod account_consistency,
114       bool dice_migration_completed)
115       : AccountReconcilor(
116             identity_manager,
117             client,
118             CreateAccountReconcilorDelegate(client,
119                                             identity_manager,
120                                             account_consistency,
121                                             dice_migration_completed)) {
122     Initialize(false /* start_reconcile_if_tokens_available */);
123   }
124 
125   // Takes ownership of |delegate|.
126   // gmock can't work with move only parameters.
DummyAccountReconcilorWithDelegate(signin::IdentityManager * identity_manager,SigninClient * client,signin::AccountReconcilorDelegate * delegate)127   DummyAccountReconcilorWithDelegate(
128       signin::IdentityManager* identity_manager,
129       SigninClient* client,
130       signin::AccountReconcilorDelegate* delegate)
131       : AccountReconcilor(
132             identity_manager,
133             client,
134             std::unique_ptr<signin::AccountReconcilorDelegate>(delegate)) {
135     Initialize(false /* start_reconcile_if_tokens_available */);
136   }
137 
138   static std::unique_ptr<signin::AccountReconcilorDelegate>
CreateAccountReconcilorDelegate(SigninClient * signin_client,signin::IdentityManager * identity_manager,signin::AccountConsistencyMethod account_consistency,bool dice_migration_completed)139   CreateAccountReconcilorDelegate(
140       SigninClient* signin_client,
141       signin::IdentityManager* identity_manager,
142       signin::AccountConsistencyMethod account_consistency,
143       bool dice_migration_completed) {
144     switch (account_consistency) {
145       case signin::AccountConsistencyMethod::kMirror:
146         return std::make_unique<signin::MirrorAccountReconcilorDelegate>(
147             identity_manager);
148       case signin::AccountConsistencyMethod::kDisabled:
149         return std::make_unique<signin::AccountReconcilorDelegate>();
150       case signin::AccountConsistencyMethod::kDice:
151 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
152         return std::make_unique<signin::DiceAccountReconcilorDelegate>(
153             signin_client, dice_migration_completed);
154 #else
155         NOTREACHED();
156         return nullptr;
157 #endif
158     }
159     NOTREACHED();
160     return nullptr;
161   }
162 };
163 
164 class MockAccountReconcilor
165     : public testing::StrictMock<DummyAccountReconcilorWithDelegate> {
166  public:
167   MockAccountReconcilor(signin::IdentityManager* identity_manager,
168                         SigninClient* client,
169                         signin::AccountConsistencyMethod account_consistency,
170                         bool dice_migration_completed);
171 
172   MockAccountReconcilor(
173       signin::IdentityManager* identity_manager,
174       SigninClient* client,
175       std::unique_ptr<signin::AccountReconcilorDelegate> delegate);
176 
177   MOCK_METHOD1(PerformMergeAction, void(const CoreAccountId& account_id));
178   MOCK_METHOD0(PerformLogoutAllAccountsAction, void());
179   MOCK_METHOD1(PerformSetCookiesAction,
180                void(const signin::MultiloginParameters& parameters));
181 };
182 
MockAccountReconcilor(signin::IdentityManager * identity_manager,SigninClient * client,signin::AccountConsistencyMethod account_consistency,bool dice_migration_completed)183 MockAccountReconcilor::MockAccountReconcilor(
184     signin::IdentityManager* identity_manager,
185     SigninClient* client,
186     signin::AccountConsistencyMethod account_consistency,
187     bool dice_migration_completed)
188     : testing::StrictMock<DummyAccountReconcilorWithDelegate>(
189           identity_manager,
190           client,
191           account_consistency,
192           dice_migration_completed) {}
193 
MockAccountReconcilor(signin::IdentityManager * identity_manager,SigninClient * client,std::unique_ptr<signin::AccountReconcilorDelegate> delegate)194 MockAccountReconcilor::MockAccountReconcilor(
195     signin::IdentityManager* identity_manager,
196     SigninClient* client,
197     std::unique_ptr<signin::AccountReconcilorDelegate> delegate)
198     : testing::StrictMock<DummyAccountReconcilorWithDelegate>(
199           identity_manager,
200           client,
201           delegate.release()) {}
202 
203 struct Cookie {
204   std::string gaia_id;
205   bool is_valid;
206 
operator ==__anonacf8a3c60111::Cookie207   bool operator==(const Cookie& other) const {
208     return gaia_id == other.gaia_id && is_valid == other.is_valid;
209   }
210 };
211 
212 // Converts CookieParams to ListedAccounts.
ListedAccountFromCookieParams(const signin::CookieParams & params,const CoreAccountId & account_id)213 gaia::ListedAccount ListedAccountFromCookieParams(
214     const signin::CookieParams& params,
215     const CoreAccountId& account_id) {
216   gaia::ListedAccount listed_account;
217   listed_account.id = account_id;
218   listed_account.email = params.email;
219   listed_account.gaia_id = params.gaia_id;
220   listed_account.raw_email = params.email;
221   listed_account.valid = params.valid;
222   listed_account.signed_out = params.signed_out;
223   listed_account.verified = params.verified;
224   return listed_account;
225 }
226 
227 }  // namespace
228 
229 class AccountReconcilorTest : public ::testing::Test {
230  protected:
231   AccountReconcilorTest();
232   ~AccountReconcilorTest() override;
233 
identity_test_env()234   signin::IdentityTestEnvironment* identity_test_env() {
235     return &identity_test_env_;
236   }
237 
task_environment()238   base::test::SingleThreadTaskEnvironment* task_environment() {
239     return &task_environment_;
240   }
241 
test_signin_client()242   TestSigninClient* test_signin_client() { return &test_signin_client_; }
histogram_tester()243   base::HistogramTester* histogram_tester() { return &histogram_tester_; }
244 
245   MockAccountReconcilor* GetMockReconcilor();
246   MockAccountReconcilor* GetMockReconcilor(
247       std::unique_ptr<signin::AccountReconcilorDelegate> delegate);
248 
249   AccountInfo ConnectProfileToAccount(const std::string& email);
250 
251   CoreAccountId PickAccountIdForAccount(const std::string& gaia_id,
252                                         const std::string& username);
253 
254   void SimulateAddAccountToCookieCompleted(AccountReconcilor* reconcilor,
255                                            const CoreAccountId& account_id,
256                                            const GoogleServiceAuthError& error);
257 
258   void SimulateSetAccountsInCookieCompleted(
259       AccountReconcilor* reconcilor,
260       signin::SetAccountsInCookieResult result);
261 
262   void SimulateLogOutFromCookieCompleted(AccountReconcilor* reconcilor,
263                                          const GoogleServiceAuthError& error);
264 
265   void SimulateCookieContentSettingsChanged(
266       content_settings::Observer* observer,
267       const ContentSettingsPattern& primary_pattern);
268 
269   void SetAccountConsistency(signin::AccountConsistencyMethod method);
270 
271   // Should never be called before |SetAccountConsistency|.
272   void SetDiceMigrationCompleted(bool dice_migration_completed);
273 
pref_service()274   PrefService* pref_service() { return &pref_service_; }
275 
DeleteReconcilor()276   void DeleteReconcilor() { mock_reconcilor_.reset(); }
277 
278   network::TestURLLoaderFactory test_url_loader_factory_;
279 
280  private:
281   base::test::SingleThreadTaskEnvironment task_environment_;
282   signin::AccountConsistencyMethod account_consistency_;
283   bool dice_migration_completed_ = false;
284   sync_preferences::TestingPrefServiceSyncable pref_service_;
285   TestSigninClient test_signin_client_;
286   signin::IdentityTestEnvironment identity_test_env_;
287   std::unique_ptr<MockAccountReconcilor> mock_reconcilor_;
288   base::HistogramTester histogram_tester_;
289 
290   DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTest);
291 };
292 
293 class AccountReconcilorMirrorTest : public AccountReconcilorTest {
294  public:
AccountReconcilorMirrorTest()295   AccountReconcilorMirrorTest() {
296     SetAccountConsistency(signin::AccountConsistencyMethod::kMirror);
297   }
298 
299  private:
300   DISALLOW_COPY_AND_ASSIGN(AccountReconcilorMirrorTest);
301 };
302 
303 // For tests that must be run with multiple account consistency methods.
304 class AccountReconcilorMethodParamTest
305     : public AccountReconcilorTest,
306       public ::testing::WithParamInterface<signin::AccountConsistencyMethod> {
307  public:
308   AccountReconcilorMethodParamTest() = default;
309 
310  private:
311   DISALLOW_COPY_AND_ASSIGN(AccountReconcilorMethodParamTest);
312 };
313 
314 INSTANTIATE_TEST_SUITE_P(Dice_Mirror,
315                          AccountReconcilorMethodParamTest,
316                          ::testing::Values(
317 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
318                              signin::AccountConsistencyMethod::kDice,
319 #endif
320                              signin::AccountConsistencyMethod::kMirror));
321 
AccountReconcilorTest()322 AccountReconcilorTest::AccountReconcilorTest()
323     : task_environment_(
324           base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME),
325       account_consistency_(signin::AccountConsistencyMethod::kDisabled),
326       test_signin_client_(&pref_service_, &test_url_loader_factory_),
327       identity_test_env_(/*test_url_loader_factory=*/nullptr,
328                          &pref_service_,
329                          account_consistency_,
330                          &test_signin_client_) {
331   signin::SetListAccountsResponseHttpNotFound(&test_url_loader_factory_);
332 
333   // The reconcilor should not be built before the test can set the account
334   // consistency method.
335   EXPECT_FALSE(mock_reconcilor_);
336 }
337 
GetMockReconcilor()338 MockAccountReconcilor* AccountReconcilorTest::GetMockReconcilor() {
339   if (!mock_reconcilor_) {
340     mock_reconcilor_ = std::make_unique<MockAccountReconcilor>(
341         identity_test_env_.identity_manager(), &test_signin_client_,
342         account_consistency_, dice_migration_completed_);
343   }
344 
345   return mock_reconcilor_.get();
346 }
347 
GetMockReconcilor(std::unique_ptr<signin::AccountReconcilorDelegate> delegate)348 MockAccountReconcilor* AccountReconcilorTest::GetMockReconcilor(
349     std::unique_ptr<signin::AccountReconcilorDelegate> delegate) {
350   mock_reconcilor_ = std::make_unique<MockAccountReconcilor>(
351       identity_test_env_.identity_manager(), &test_signin_client_,
352       std::move(delegate));
353 
354   return mock_reconcilor_.get();
355 }
356 
~AccountReconcilorTest()357 AccountReconcilorTest::~AccountReconcilorTest() {
358   if (mock_reconcilor_)
359     mock_reconcilor_->Shutdown();
360   test_signin_client_.Shutdown();
361 }
362 
ConnectProfileToAccount(const std::string & email)363 AccountInfo AccountReconcilorTest::ConnectProfileToAccount(
364     const std::string& email) {
365   AccountInfo account_info =
366       identity_test_env()->MakePrimaryAccountAvailable(email);
367   return account_info;
368 }
369 
PickAccountIdForAccount(const std::string & gaia_id,const std::string & username)370 CoreAccountId AccountReconcilorTest::PickAccountIdForAccount(
371     const std::string& gaia_id,
372     const std::string& username) {
373   return identity_test_env()->identity_manager()->PickAccountIdForAccount(
374       gaia_id, username);
375 }
376 
SimulateAddAccountToCookieCompleted(AccountReconcilor * reconcilor,const CoreAccountId & account_id,const GoogleServiceAuthError & error)377 void AccountReconcilorTest::SimulateAddAccountToCookieCompleted(
378     AccountReconcilor* reconcilor,
379     const CoreAccountId& account_id,
380     const GoogleServiceAuthError& error) {
381   reconcilor->OnAddAccountToCookieCompleted(account_id, error);
382 }
383 
SimulateSetAccountsInCookieCompleted(AccountReconcilor * reconcilor,signin::SetAccountsInCookieResult result)384 void AccountReconcilorTest::SimulateSetAccountsInCookieCompleted(
385     AccountReconcilor* reconcilor,
386     signin::SetAccountsInCookieResult result) {
387   reconcilor->OnSetAccountsInCookieCompleted(result);
388 }
389 
SimulateLogOutFromCookieCompleted(AccountReconcilor * reconcilor,const GoogleServiceAuthError & error)390 void AccountReconcilorTest::SimulateLogOutFromCookieCompleted(
391     AccountReconcilor* reconcilor,
392     const GoogleServiceAuthError& error) {
393   reconcilor->OnLogOutFromCookieCompleted(error);
394 }
395 
SimulateCookieContentSettingsChanged(content_settings::Observer * observer,const ContentSettingsPattern & primary_pattern)396 void AccountReconcilorTest::SimulateCookieContentSettingsChanged(
397     content_settings::Observer* observer,
398     const ContentSettingsPattern& primary_pattern) {
399   observer->OnContentSettingChanged(
400       primary_pattern, ContentSettingsPattern::Wildcard(),
401       ContentSettingsType::COOKIES, std::string());
402 }
403 
SetAccountConsistency(signin::AccountConsistencyMethod method)404 void AccountReconcilorTest::SetAccountConsistency(
405     signin::AccountConsistencyMethod method) {
406   account_consistency_ = method;
407   dice_migration_completed_ =
408       account_consistency_ == signin::AccountConsistencyMethod::kDice;
409 }
410 
SetDiceMigrationCompleted(bool dice_migration_completed)411 void AccountReconcilorTest::SetDiceMigrationCompleted(
412     bool dice_migration_completed) {
413   DCHECK_EQ(signin::AccountConsistencyMethod::kDice, account_consistency_);
414   dice_migration_completed_ = dice_migration_completed;
415 }
416 
TEST_F(AccountReconcilorTest,Basic)417 TEST_F(AccountReconcilorTest, Basic) {
418   AccountReconcilor* reconcilor = GetMockReconcilor();
419   ASSERT_TRUE(reconcilor);
420 }
421 
422 enum class IsFirstReconcile {
423   kBoth = 0,
424   kFirst,
425   kNotFirst,
426 };
427 
428 struct AccountReconcilorTestTableParam {
429   const char* tokens;
430   const char* cookies;
431   IsFirstReconcile is_first_reconcile;
432   const char* gaia_api_calls;
433   const char* tokens_after_reconcile;
434   const char* cookies_after_reconcile;
435   const char* gaia_api_calls_multilogin;
436   const char* tokens_after_reconcile_multilogin;
437   const char* cookies_after_reconcile_multilogin;
438   // Int represents AccountReconcilorDelegate::InconsistencyReason.
439   const int inconsistency_reason;
440 };
441 
GenerateTestCasesFromParams(const std::vector<AccountReconcilorTestTableParam> & params)442 std::vector<AccountReconcilorTestTableParam> GenerateTestCasesFromParams(
443     const std::vector<AccountReconcilorTestTableParam>& params) {
444   std::vector<AccountReconcilorTestTableParam> return_params;
445   for (const AccountReconcilorTestTableParam& param : params) {
446     if (param.is_first_reconcile == IsFirstReconcile::kBoth) {
447       AccountReconcilorTestTableParam param_true = param;
448       param_true.is_first_reconcile = IsFirstReconcile::kFirst;
449       AccountReconcilorTestTableParam param_false = param;
450       param_false.is_first_reconcile = IsFirstReconcile::kNotFirst;
451       return_params.push_back(param_true);
452       return_params.push_back(param_false);
453     } else {
454       return_params.push_back(param);
455     }
456   }
457   return return_params;
458 }
459 
460 struct ForceDiceMigrationTestTableParam {
461   const char* tokens;
462   const char* cookies;
463   const char* gaia_api_calls;
464   const char* tokens_after_reconcile;
465   const char* cookies_after_reconcile;
466   RevokeTokenAction revoke_token_action;
467 };
468 
469 // Pretty prints a AccountReconcilorTestTableParam. Used by gtest.
PrintTo(const AccountReconcilorTestTableParam & param,::std::ostream * os)470 void PrintTo(const AccountReconcilorTestTableParam& param, ::std::ostream* os) {
471   *os << "Tokens: " << param.tokens << ". Cookies: " << param.cookies
472       << ". First reconcile: "
473       << (param.is_first_reconcile == IsFirstReconcile::kFirst ? "true"
474                                                                : "false");
475 }
476 
477 class BaseAccountReconcilorTestTable : public AccountReconcilorTest {
478  protected:
BaseAccountReconcilorTestTable(const AccountReconcilorTestTableParam & param)479   BaseAccountReconcilorTestTable(const AccountReconcilorTestTableParam& param)
480       : BaseAccountReconcilorTestTable(param.tokens,
481                                        param.cookies,
482                                        param.is_first_reconcile,
483                                        param.gaia_api_calls,
484                                        param.tokens_after_reconcile,
485                                        param.cookies_after_reconcile) {}
486 
BaseAccountReconcilorTestTable(const char * tokens,const char * cookies,IsFirstReconcile is_first_reconcile,const char * gaia_api_calls,const char * tokens_after_reconcile,const char * cookies_after_reconcile)487   BaseAccountReconcilorTestTable(const char* tokens,
488                                  const char* cookies,
489                                  IsFirstReconcile is_first_reconcile,
490                                  const char* gaia_api_calls,
491                                  const char* tokens_after_reconcile,
492                                  const char* cookies_after_reconcile)
493       : tokens_(tokens),
494         cookies_(cookies),
495         is_first_reconcile_(is_first_reconcile),
496         gaia_api_calls_(gaia_api_calls),
497         tokens_after_reconcile_(tokens_after_reconcile),
498         cookies_after_reconcile_(cookies_after_reconcile) {
499     accounts_['A'] = {"a@gmail.com",
500                       signin::GetTestGaiaIdForEmail("a@gmail.com")};
501     accounts_['B'] = {"b@gmail.com",
502                       signin::GetTestGaiaIdForEmail("b@gmail.com")};
503     accounts_['C'] = {"c@gmail.com",
504                       signin::GetTestGaiaIdForEmail("c@gmail.com")};
505   }
506 
507   struct Account {
508     std::string email;
509     std::string gaia_id;
510   };
511 
512   struct Token {
513     std::string gaia_id;
514     std::string email;
515     bool is_authenticated;
516     bool has_error;
517   };
518 
519   // Build Tokens from string.
ParseTokenString(const char * token_string)520   std::vector<Token> ParseTokenString(const char* token_string) {
521     std::vector<Token> parsed_tokens;
522     bool is_authenticated = false;
523     bool has_error = false;
524     for (int i = 0; token_string[i] != '\0'; ++i) {
525       char token_code = token_string[i];
526       if (token_code == '*') {
527         is_authenticated = true;
528         continue;
529       }
530       if (token_code == 'x') {
531         has_error = true;
532         continue;
533       }
534       parsed_tokens.push_back({accounts_[token_code].gaia_id,
535                                accounts_[token_code].email, is_authenticated,
536                                has_error});
537       is_authenticated = false;
538       has_error = false;
539     }
540     return parsed_tokens;
541   }
542 
543   // Build Cookies from string.
ParseCookieString(const char * cookie_string)544   std::vector<Cookie> ParseCookieString(const char* cookie_string) {
545     std::vector<Cookie> parsed_cookies;
546     bool valid = true;
547     for (int i = 0; cookie_string[i] != '\0'; ++i) {
548       char cookie_code = cookie_string[i];
549       if (cookie_code == 'x') {
550         valid = false;
551         continue;
552       }
553       parsed_cookies.push_back({accounts_[cookie_code].gaia_id, valid});
554       valid = true;
555     }
556     return parsed_cookies;
557   }
558 
559   // Checks that the tokens in the TokenService match the tokens.
VerifyCurrentTokens(const std::vector<Token> & tokens)560   void VerifyCurrentTokens(const std::vector<Token>& tokens) {
561     auto* identity_manager = identity_test_env()->identity_manager();
562     EXPECT_EQ(identity_manager->GetAccountsWithRefreshTokens().size(),
563               tokens.size());
564 
565     bool authenticated_account_found = false;
566     for (const Token& token : tokens) {
567       CoreAccountId account_id =
568           PickAccountIdForAccount(token.gaia_id, token.email);
569       EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id));
570       EXPECT_EQ(
571           token.has_error,
572           identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
573               account_id));
574       if (token.is_authenticated) {
575         EXPECT_EQ(account_id, identity_manager->GetPrimaryAccountId());
576         authenticated_account_found = true;
577       }
578     }
579     if (!authenticated_account_found)
580       EXPECT_EQ(CoreAccountId(), identity_manager->GetPrimaryAccountId());
581   }
582 
SetupTokens(const char * tokens_string)583   void SetupTokens(const char* tokens_string) {
584     std::vector<Token> tokens = ParseTokenString(tokens_string);
585     Token primary_account;
586     for (const Token& token : tokens) {
587       CoreAccountId account_id;
588       if (token.is_authenticated) {
589         account_id = ConnectProfileToAccount(token.email).account_id;
590       } else {
591         account_id =
592             identity_test_env()->MakeAccountAvailable(token.email).account_id;
593       }
594       if (token.has_error) {
595         signin::UpdatePersistentErrorOfRefreshTokenForAccount(
596             identity_test_env()->identity_manager(), account_id,
597             GoogleServiceAuthError(
598                 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
599       }
600     }
601     VerifyCurrentTokens(tokens);
602   }
603 
ConfigureCookieManagerService(const std::vector<Cookie> & cookies)604   void ConfigureCookieManagerService(const std::vector<Cookie>& cookies) {
605     std::vector<signin::CookieParams> cookie_params;
606     for (const auto& cookie : cookies) {
607       std::string gaia_id = cookie.gaia_id;
608 
609       // Figure the account token of this specific account id,
610       // ie 'A', 'B', or 'C'.
611       char account_key = '\0';
612       for (const auto& account : accounts_) {
613         if (account.second.gaia_id == gaia_id) {
614           account_key = account.first;
615           break;
616         }
617       }
618       ASSERT_NE(account_key, '\0');
619 
620       cookie_params.push_back({accounts_[account_key].email, gaia_id,
621                                cookie.is_valid, false /* signed_out */,
622                                true /* verified */});
623     }
624     signin::SetListAccountsResponseWithParams(cookie_params,
625                                               &test_url_loader_factory_);
626     identity_test_env()->SetFreshnessOfAccountsInGaiaCookie(false);
627   }
628 
629 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
RunReconcile()630   void RunReconcile() {
631     // Setup cookies.
632     std::vector<Cookie> cookies = ParseCookieString(cookies_);
633     ConfigureCookieManagerService(cookies);
634 
635     // Setup tokens. This triggers listing cookies so we need to setup cookies
636     // before that.
637     SetupTokens(tokens_);
638 
639     // Call list accounts now so that the next call completes synchronously.
640     identity_test_env()->identity_manager()->GetAccountsInCookieJar();
641     base::RunLoop().RunUntilIdle();
642 
643     // Setup expectations.
644     testing::InSequence mock_sequence;
645     bool logout_action = false;
646     for (int i = 0; gaia_api_calls_[i] != '\0'; ++i) {
647       if (gaia_api_calls_[i] == 'X') {
648         logout_action = true;
649         EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
650             .Times(1);
651         cookies.clear();
652         continue;
653       }
654       std::string cookie(1, gaia_api_calls_[i]);
655       CoreAccountId account_id_for_cookie = PickAccountIdForAccount(
656           accounts_[cookie[0]].gaia_id, accounts_[cookie[0]].email);
657       EXPECT_CALL(*GetMockReconcilor(),
658                   PerformMergeAction(account_id_for_cookie))
659           .Times(1);
660       // MergeSession fixes an existing cookie or appends it at the end.
661       auto it =
662           std::find(cookies.begin(), cookies.end(),
663                     Cookie{accounts_[cookie[0]].gaia_id, false /* is_valid */});
664       if (it == cookies.end())
665         cookies.push_back({accounts_[cookie[0]].gaia_id, true});
666       else
667         it->is_valid = true;
668     }
669     if (!logout_action) {
670       EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
671           .Times(0);
672     }
673 
674     // Check the expected cookies after reconcile.
675     std::vector<Cookie> expected_cookies =
676         ParseCookieString(cookies_after_reconcile_);
677     ASSERT_EQ(expected_cookies, cookies);
678 
679     // Reconcile.
680     AccountReconcilor* reconcilor = GetMockReconcilor();
681     ASSERT_TRUE(reconcilor->first_execution_);
682     reconcilor->first_execution_ =
683         is_first_reconcile_ == IsFirstReconcile::kFirst;
684     ASSERT_TRUE(reconcilor->delegate_->IsAccountConsistencyEnforced());
685     reconcilor->StartReconcile();
686     for (int i = 0; gaia_api_calls_[i] != '\0'; ++i) {
687       if (gaia_api_calls_[i] == 'X') {
688         SimulateLogOutFromCookieCompleted(
689             reconcilor, GoogleServiceAuthError::AuthErrorNone());
690         continue;
691       }
692       CoreAccountId account_id =
693           PickAccountIdForAccount(accounts_[gaia_api_calls_[i]].gaia_id,
694                                   accounts_[gaia_api_calls_[i]].email);
695       SimulateAddAccountToCookieCompleted(
696           reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone());
697     }
698     ASSERT_FALSE(reconcilor->is_reconcile_started_);
699 
700     if (tokens_ == tokens_after_reconcile_) {
701       EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
702     } else {
703       // If the tokens were changed by the reconcile, a new reconcile should be
704       // scheduled.
705       EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED,
706                 reconcilor->GetState());
707     }
708 
709     VerifyCurrentTokens(ParseTokenString(tokens_after_reconcile_));
710 
711     testing::Mock::VerifyAndClearExpectations(GetMockReconcilor());
712 
713     // Another reconcile is sometimes triggered if Chrome accounts have changed.
714     // Allow it to finish.
715     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_))
716         .WillRepeatedly(testing::Return());
717     EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
718         .WillRepeatedly(testing::Return());
719     ConfigureCookieManagerService({});
720     base::RunLoop().RunUntilIdle();
721   }
722 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
723 
GaiaIdForAccountKey(char account_key)724   std::string GaiaIdForAccountKey(char account_key) {
725     return accounts_[account_key].gaia_id;
726   }
727 
728   std::map<char, Account> accounts_;
729   const char* tokens_;
730   const char* cookies_;
731   IsFirstReconcile is_first_reconcile_;
732   const char* gaia_api_calls_;
733   const char* tokens_after_reconcile_;
734   const char* cookies_after_reconcile_;
735 };
736 
737 // Parameterized version of AccountReconcilorTest.
738 class AccountReconcilorTestTable
739     : public BaseAccountReconcilorTestTable,
740       public ::testing::WithParamInterface<AccountReconcilorTestTableParam> {
741  protected:
AccountReconcilorTestTable()742   AccountReconcilorTestTable() : BaseAccountReconcilorTestTable(GetParam()) {}
743 
744   // Checks that reconcile is idempotent.
CheckReconcileIdempotent(const std::vector<AccountReconcilorTestTableParam> & params,const AccountReconcilorTestTableParam & param,bool multilogin)745   void CheckReconcileIdempotent(
746       const std::vector<AccountReconcilorTestTableParam>& params,
747       const AccountReconcilorTestTableParam& param,
748       bool multilogin) {
749     // Simulate another reconcile based on the results of this one: find the
750     // corresponding row in the table and check that it does nothing.
751     for (const AccountReconcilorTestTableParam& row : params) {
752       if (row.is_first_reconcile == IsFirstReconcile::kFirst)
753         continue;
754 
755       if (!((strcmp(row.tokens, param.tokens_after_reconcile) == 0 &&
756              strcmp(row.cookies, param.cookies_after_reconcile) == 0 &&
757              !multilogin) ||
758             (strcmp(row.tokens, param.tokens_after_reconcile_multilogin) == 0 &&
759              strcmp(row.cookies, param.cookies_after_reconcile_multilogin) ==
760                  0 &&
761              multilogin))) {
762         continue;
763       }
764       if (multilogin) {
765         EXPECT_STREQ(row.tokens, row.tokens_after_reconcile_multilogin);
766         EXPECT_STREQ(row.cookies, row.cookies_after_reconcile_multilogin);
767       } else {
768         EXPECT_STREQ(row.tokens, row.tokens_after_reconcile);
769         EXPECT_STREQ(row.cookies, row.cookies_after_reconcile);
770       }
771       return;
772     }
773 
774     ADD_FAILURE() << "Could not check that reconcile is idempotent.";
775   }
776 };
777 
778 #if !defined(OS_CHROMEOS)
779 
TEST_F(AccountReconcilorMirrorTest,IdentityManagerRegistration)780 TEST_F(AccountReconcilorMirrorTest, IdentityManagerRegistration) {
781   AccountReconcilor* reconcilor = GetMockReconcilor();
782   ASSERT_TRUE(reconcilor);
783   ASSERT_FALSE(reconcilor->IsRegisteredWithIdentityManager());
784 
785   identity_test_env()->MakePrimaryAccountAvailable("user@gmail.com");
786   ASSERT_TRUE(reconcilor->IsRegisteredWithIdentityManager());
787 
788   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
789 
790   identity_test_env()->ClearPrimaryAccount();
791   ASSERT_FALSE(reconcilor->IsRegisteredWithIdentityManager());
792 }
793 
TEST_F(AccountReconcilorMirrorTest,Reauth)794 TEST_F(AccountReconcilorMirrorTest, Reauth) {
795   const std::string email = "user@gmail.com";
796   AccountInfo account_info = ConnectProfileToAccount(email);
797 
798   AccountReconcilor* reconcilor = GetMockReconcilor();
799   ASSERT_TRUE(reconcilor);
800   ASSERT_TRUE(reconcilor->IsRegisteredWithIdentityManager());
801 
802   // Simulate reauth.  The state of the reconcilor should not change.
803   auto* account_mutator =
804       identity_test_env()->identity_manager()->GetPrimaryAccountMutator();
805   DCHECK(account_mutator);
806   account_mutator->SetPrimaryAccount(account_info.account_id);
807 
808   ASSERT_TRUE(reconcilor->IsRegisteredWithIdentityManager());
809 }
810 
811 #endif  // !defined(OS_CHROMEOS)
812 
TEST_F(AccountReconcilorMirrorTest,ProfileAlreadyConnected)813 TEST_F(AccountReconcilorMirrorTest, ProfileAlreadyConnected) {
814   ConnectProfileToAccount("user@gmail.com");
815 
816   AccountReconcilor* reconcilor = GetMockReconcilor();
817   ASSERT_TRUE(reconcilor);
818   ASSERT_TRUE(reconcilor->IsRegisteredWithIdentityManager());
819 }
820 
821 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
822 
823 namespace {
FakeSetAccountsInCookie(const signin::MultiloginParameters & parameters,const std::vector<Cookie> & cookies_before_reconcile)824 std::vector<Cookie> FakeSetAccountsInCookie(
825     const signin::MultiloginParameters& parameters,
826     const std::vector<Cookie>& cookies_before_reconcile) {
827   std::vector<Cookie> cookies_after_reconcile;
828   if (parameters.mode ==
829       gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER) {
830     for (const CoreAccountId& account : parameters.accounts_to_send) {
831       cookies_after_reconcile.push_back({account.ToString(), true});
832     }
833   } else {
834     std::set<CoreAccountId> accounts_set(parameters.accounts_to_send.begin(),
835                                          parameters.accounts_to_send.end());
836     cookies_after_reconcile = cookies_before_reconcile;
837     for (Cookie& param : cookies_after_reconcile) {
838       if (accounts_set.find(CoreAccountId(param.gaia_id)) !=
839           accounts_set.end()) {
840         param.is_valid = true;
841         accounts_set.erase(CoreAccountId(param.gaia_id));
842       } else {
843         param.is_valid = false;
844       }
845     }
846     for (const CoreAccountId& account : accounts_set) {
847       cookies_after_reconcile.push_back({account.ToString(), true});
848     }
849   }
850   return cookies_after_reconcile;
851 }
852 }  // namespace
853 
854 // clang-format off
855 const std::vector<AccountReconcilorTestTableParam> kDiceParams = {
856     // This table encodes the initial state and expectations of a reconcile.
857     // The syntax is:
858     // - Tokens:
859     //   A, B, C: Accounts for which we have a token in Chrome.
860     //   *: The next account is the main Chrome account (i.e. in
861     //   IdentityManager).
862     //   x: The next account has a token error.
863     // - API calls:
864     //   U: Multilogin with mode UPDATE
865     //   P: Multilogin with mode PRESERVE
866     //   X: Logout all accounts.
867     //   A, B, C: Merge account.
868 
869     // - Cookies:
870     //   A, B, C: Accounts in the Gaia cookie (returned by ListAccounts).
871     //   x: The next cookie is marked "invalid".
872     // - First Run: true if this is the first reconcile (i.e. Chrome startup).
873     // -------------------------------------------------------------------------
874     // Tokens|Cookies|First Run|Gaia calls|Tokens aft.|Cookies aft.|M.calls| M.Tokens aft.| M.Cookies aft.| AccountReconcilorDelegate::InconsistencyReason |
875     // -------------------------------------------------------------------------
876 
877     // First reconcile (Chrome restart): Rebuild the Gaia cookie to match the
878     // tokens. Make the Sync account the default account in the Gaia cookie.
879     // Sync enabled.
880     {  "",      "A",   IsFirstReconcile::kBoth,       "X",   "",      "",    "P",   "",     "xA",   3},
881     {  "*AB",   "AB",  IsFirstReconcile::kBoth,       "",    "*AB",   "AB",  "",    "*AB",  "AB",   0},
882     {  "*A",    "A",   IsFirstReconcile::kBoth,       "",    "*A",    "A",   "",    "*A" ,  "A",    0},
883 
884     {  "*A",    "",    IsFirstReconcile::kFirst,      "A",   "*A",    "A",   "UA",  "*A" ,  "A",    1},
885     {  "*A",    "",    IsFirstReconcile::kNotFirst,   "A",   "*A",    "A",   "PA",  "*A" ,  "A",    1},
886 
887     {  "*A",    "B",   IsFirstReconcile::kFirst,      "XA",  "*A",    "A",   "UA",  "*A" ,  "A",    1},
888     {  "*A",    "B",   IsFirstReconcile::kNotFirst,   "XA",  "*A",    "A",   "PA",  "*A" ,  "xBA",  1},
889 
890     {  "*A",    "AB",  IsFirstReconcile::kBoth,       "XA",  "*A",    "A",   "PA",  "*A" ,  "AxB",  5},
891 
892     {  "*AB",   "BA",  IsFirstReconcile::kFirst,      "XAB", "*AB",   "AB",  "UAB", "*AB",  "AB",   7},
893     {  "*AB",   "BA",  IsFirstReconcile::kNotFirst,   "",    "*AB",   "BA",  "",    "*AB",  "BA",   0},
894 
895     {  "*AB",   "A",   IsFirstReconcile::kBoth,       "B",   "*AB",   "AB",  "PAB", "*AB",  "AB",   4},
896 
897     {  "*AB",   "B",   IsFirstReconcile::kFirst,      "XAB", "*AB",   "AB",  "UAB", "*AB",  "AB",   1},
898     {  "*AB",   "B",   IsFirstReconcile::kNotFirst,   "A",   "*AB",   "BA",  "PAB", "*AB",  "BA",   1},
899 
900     {  "*AB",   "",    IsFirstReconcile::kFirst,      "AB",  "*AB",   "AB",  "UAB", "*AB",  "AB",   1},
901     {  "*AB",   "",    IsFirstReconcile::kNotFirst,   "AB",  "*AB",   "AB",  "PAB", "*AB",  "AB",   1},
902     // Sync enabled, token error on primary.
903     {  "*xAB",  "AB",  IsFirstReconcile::kBoth,       "X",   "*xA",   "" ,   "PB",  "*xAB", "xAB",  2},
904     {  "*xAB",  "BA",  IsFirstReconcile::kBoth,       "XB",  "*xAB",  "B",   "PB",  "*xAB", "BxA",  2},
905     {  "*xAB",  "A",   IsFirstReconcile::kBoth,       "X",   "*xA",   "" ,   "PB",  "*xAB", "xAB",  2},
906     {  "*xAB",  "B",   IsFirstReconcile::kBoth,       "",    "*xAB",  "B",   "",    "*xAB", "B"  ,  0},
907     {  "*xAB",  "",    IsFirstReconcile::kBoth,       "B",   "*xAB",  "B",   "PB",  "*xAB", "B"  ,  0},
908     // Sync enabled, token error on secondary.
909     {  "*AxB",  "AB",  IsFirstReconcile::kBoth,       "XA",  "*A",    "A",   "PA",  "*A",   "AxB",  5},
910 
911     {  "*AxB",  "BA",  IsFirstReconcile::kFirst,      "XA",  "*A",    "A",   "UA",  "*A",   "A"  ,  5},
912     {  "*AxB",  "BA",  IsFirstReconcile::kNotFirst,   "XA",  "*A",    "A",   "PA",  "*A",   "xBA",  5},
913 
914     {  "*AxB",  "A",   IsFirstReconcile::kBoth,       "",    "*A",    "A",   "",    "*A",   "A"  ,  0 },
915 
916     {  "*AxB",  "B",   IsFirstReconcile::kFirst,      "XA",  "*A",    "A",   "UA",  "*A",   "A"  ,  1},
917     {  "*AxB",  "B",   IsFirstReconcile::kNotFirst,   "XA",  "*A",    "A",   "PA",  "*A",   "xBA",  1 },
918 
919     {  "*AxB",  "",    IsFirstReconcile::kFirst,      "A",   "*A",    "A",   "UA",  "*A",   "A",    1},
920     {  "*AxB",  "",    IsFirstReconcile::kNotFirst,   "A",   "*A",    "A",   "PA",  "*A",   "A",    1},
921     // Sync enabled, token error on both accounts.
922     {  "*xAxB", "AB",  IsFirstReconcile::kBoth,       "X",   "*xA",   "",    "P",   "*xA",  "xAxB", 2},
923     {  "*xAxB", "BA",  IsFirstReconcile::kBoth,       "X",   "*xA",   "",    "P",   "*xA",  "xBxA", 2},
924     {  "*xAxB", "A",   IsFirstReconcile::kBoth,       "X",   "*xA",   "",    "P",   "*xA",  "xA",   2},
925     {  "*xAxB", "B",   IsFirstReconcile::kBoth,       "X",   "*xA",   "",    "P",   "*xA",  "xB",   5},
926     {  "*xAxB", "",    IsFirstReconcile::kBoth,       "",    "*xA",   "",    "",    "*xA",  "",     0},
927     // Sync disabled.
928     {  "AB",    "AB",  IsFirstReconcile::kBoth,       "",    "AB",    "AB",  "",    "AB",   "AB",   0},
929     {  "AB",    "BA",  IsFirstReconcile::kBoth,       "",    "AB",    "BA",  "",    "AB",   "BA",   0},
930     {  "AB",    "A",   IsFirstReconcile::kBoth,       "B",   "AB",    "AB",  "PAB", "AB",   "AB",   4},
931     {  "AB",    "B",   IsFirstReconcile::kBoth,       "A",   "AB",    "BA",  "PAB", "AB",   "BA",   4},
932     {  "AB",    "",    IsFirstReconcile::kBoth,       "AB",  "AB",    "AB",  "PAB", "AB",   "AB",   0},
933     // Sync disabled, token error on first account.
934     {  "xAB",   "AB",  IsFirstReconcile::kFirst,      "XB",  "B",     "B",   "PB",  "B",    "xAB",  3},
935     {  "xAB",   "AB",  IsFirstReconcile::kNotFirst,   "X",   "",      "" ,   "PB",  "B",    "xAB",  3},
936 
937     {  "xAB",   "BA",  IsFirstReconcile::kBoth,       "XB",  "B",     "B",   "PB",  "B",    "BxA",  5},
938 
939     {  "xAB",   "A",   IsFirstReconcile::kFirst,      "XB",  "B",     "B",   "PB",  "B",    "xAB",  3},
940     {  "xAB",   "A",   IsFirstReconcile::kNotFirst,   "X",   "",      "" ,   "PB",  "B",    "xAB",  3},
941 
942     {  "xAB",   "B",   IsFirstReconcile::kBoth,       "",    "B",     "B",   "",    "B",    "B",    0},
943 
944     {  "xAB",   "",    IsFirstReconcile::kBoth,       "B",   "B",     "B",   "PB",  "B",    "B",    0},
945     // Sync disabled, token error on second account   .
946     {  "AxB",   "AB",  IsFirstReconcile::kBoth,       "XA",  "A",     "A",   "PA",  "A",    "AxB",  5},
947 
948     {  "AxB",   "BA",  IsFirstReconcile::kFirst,      "XA",  "A",     "A",   "PA",  "A",    "xBA",  3},
949     {  "AxB",   "BA",  IsFirstReconcile::kNotFirst,   "X",   "",      "" ,   "PA",  "A",    "xBA",  3},
950 
951     {  "AxB",   "A",   IsFirstReconcile::kBoth,       "",    "A",     "A",   "",    "A",    "A",    0},
952 
953     {  "AxB",   "B",   IsFirstReconcile::kFirst,      "XA",  "A",     "A",   "PA",  "A",    "xBA",  3},
954     {  "AxB",   "B",   IsFirstReconcile::kNotFirst,   "X",   "",      "" ,   "PA",  "A",    "xBA",  3},
955 
956     {  "AxB",   "",    IsFirstReconcile::kBoth,       "A",   "A",     "A",   "PA",  "A",    "A",    0},
957     // Sync disabled, token error on both accounts.
958     {  "xAxB",  "AB",  IsFirstReconcile::kBoth,       "X",   "",      "",    "P",   "",     "xAxB", 3},
959     {  "xAxB",  "BA",  IsFirstReconcile::kBoth,       "X",   "",      "",    "P",   "",     "xBxA", 3},
960     {  "xAxB",  "A",   IsFirstReconcile::kBoth,       "X",   "",      "",    "P",   "",     "xA",   3},
961     {  "xAxB",  "B",   IsFirstReconcile::kBoth,       "X",   "",      "",    "P",   "",     "xB",   3},
962     {  "xAxB",  "",    IsFirstReconcile::kBoth,       "",    "",      "",    "",    "",     "",     0},
963     // Account marked as invalid in cookies.
964     // No difference between cookies and tokens, do not do do anything.
965     // Do not logout. Regression tests for http://crbug.com/854799
966     {  "",     "xA",   IsFirstReconcile::kBoth,       "",    "",      "xA",   "",   "",     "xA",   0},
967     {  "",     "xAxB", IsFirstReconcile::kBoth,       "",    "",      "xAxB", "",   "",     "xAxB", 0},
968     {  "xA",   "xA",   IsFirstReconcile::kBoth,       "",    "",      "xA",   "",   "",     "xA",   0},
969     {  "xAB",  "xAB",  IsFirstReconcile::kBoth,       "",    "B",     "xAB",  "",   "B",    "xAB",  0},
970     {  "AxB",  "AxC",  IsFirstReconcile::kBoth,       "",    "A",     "AxC",  "",   "A",    "AxC",  0},
971     {  "B",    "xAB",  IsFirstReconcile::kBoth,       "",    "B",     "xAB",  "",   "B",    "xAB",  0},
972     {  "*xA",  "xA",   IsFirstReconcile::kBoth,       "",    "*xA",   "xA",   "",   "*xA",  "xA",   0},
973     {  "*xA",  "xB",   IsFirstReconcile::kBoth,       "",    "*xA",   "xB",   "",   "*xA",  "xB",   0},
974     {  "*xAB", "xAB",  IsFirstReconcile::kBoth,       "",    "*xAB",  "xAB",  "",   "*xAB", "xAB",  0},
975     {  "*AxB", "xBA",  IsFirstReconcile::kNotFirst,   "",    "*A",    "xBA",  "",   "*A",   "xBA",  0},
976     // Appending a new cookie after the invalid one.
977     {  "B",    "xA",   IsFirstReconcile::kBoth,       "B",   "B",     "xAB",  "PB", "B",    "xAB",  4},
978     {  "xAB",  "xA",   IsFirstReconcile::kBoth,       "B",   "B",     "xAB",  "PB", "B",    "xAB",  4},
979     // Refresh existing cookies.
980     {  "AB",   "xAB",  IsFirstReconcile::kBoth,       "A",   "AB",    "AB",   "PAB","AB",   "AB",   4},
981     {  "*AB",  "xBxA", IsFirstReconcile::kNotFirst,   "BA",  "*AB",   "BA",   "PAB","*AB",  "BA",   1},
982     // Appending and invalidating cookies at the same time.
983     // Difference should disappear after migrating to Multilogin.
984     {  "xAB",  "xAC",  IsFirstReconcile::kFirst,      "XB",  "B",     "B",    "PB", "B",    "xAxCB", 6},
985     {  "xAB",  "xAC",  IsFirstReconcile::kNotFirst,   "X",   "",      "",     "PB", "B",    "xAxCB", 6},
986 
987     {  "xAB",  "AxC",  IsFirstReconcile::kFirst,      "XB", "B",      "B",    "PB", "B",    "xAxCB", 3},
988     {  "xAB",  "AxC",  IsFirstReconcile::kNotFirst,   "X",  "",       "",     "PB", "B",    "xAxCB", 3},
989 
990     {  "*xAB", "xABC", IsFirstReconcile::kFirst,      "XB", "*xAB",   "B",    "PB", "*xAB", "xABxC", 5},
991     {  "*xAB", "xABC", IsFirstReconcile::kNotFirst,   "X",  "*xA",    "",     "PB", "*xAB", "xABxC", 5},
992 
993     {  "xAB",  "xABC", IsFirstReconcile::kFirst,      "XB", "B",      "B",    "PB", "B",    "xABxC", 5},
994     {  "xAB",  "xABC", IsFirstReconcile::kNotFirst,   "X",  "",       "",     "PB", "B",    "xABxC", 5},
995 
996     // Miscellaneous cases.
997     // Check that unknown Gaia accounts are signed out.
998     {  "*A",   "AB",   IsFirstReconcile::kBoth,       "XA", "*A",     "A",    "PA", "*A",   "AxB",   5},
999     // Check that Gaia default account is kept in first position.
1000     {  "AB",   "BC",   IsFirstReconcile::kBoth,       "XBA","AB",     "BA",   "PAB","AB",   "BxCA",  6},
1001     // Check that Gaia cookie order is preserved for B.
1002     {  "*ABC", "CB",   IsFirstReconcile::kFirst,      "XABC","*ABC",  "ABC",  "UABC","*ABC","ABC",   1},
1003     // Required for idempotency check.
1004     {  "",     "",     IsFirstReconcile::kNotFirst,   "",   "",       "",     "",   "",     "",      0},
1005     {  "",     "xA",   IsFirstReconcile::kNotFirst,   "",   "",       "xA",   "",   "",     "xA",    0},
1006     {  "",     "xB",   IsFirstReconcile::kNotFirst,   "",   "",       "xB",   "",   "",     "xB",    0},
1007     {  "",     "xAxB", IsFirstReconcile::kNotFirst,   "",   "",       "xAxB", "",   "",     "xAxB",  0},
1008     {  "",     "xBxA", IsFirstReconcile::kNotFirst,   "",   "",       "xBxA", "",   "",     "xBxA",  0},
1009     {  "*A",   "A",    IsFirstReconcile::kNotFirst,   "",   "*A",     "A",    "",   "*A",   "A",     0},
1010     {  "*A",   "xBA",  IsFirstReconcile::kNotFirst,   "",   "*A",     "xBA",  "",   "*A",   "xBA",   0},
1011     {  "*A",   "AxB",  IsFirstReconcile::kNotFirst,   "",   "*A",     "AxB",  "",   "*A",   "AxB",   0},
1012     {  "A",    "A",    IsFirstReconcile::kNotFirst,   "",   "A",      "A",    "",   "A",    "A",     0},
1013     {  "A",    "xBA",  IsFirstReconcile::kNotFirst,   "",   "A",      "xBA",  "",   "A",    "xBA",   0},
1014     {  "A",    "AxB",  IsFirstReconcile::kNotFirst,   "",   "A",      "AxB",  "",   "A",    "AxB",   0},
1015     {  "B",    "B",    IsFirstReconcile::kNotFirst,   "",   "B",      "B",    "",   "B",    "B",     0},
1016     {  "B",    "xAB",  IsFirstReconcile::kNotFirst,   "",   "B",      "xAB",  "",   "B",    "xAB",   0},
1017     {  "B",    "BxA",  IsFirstReconcile::kNotFirst,   "",   "B",      "BxA",  "",   "B",    "BxA",   0},
1018     {  "*xA",  "",     IsFirstReconcile::kNotFirst,   "",   "*xA",    "",     "",   "*xA",  "",      0},
1019     {  "*xA",  "xAxB", IsFirstReconcile::kNotFirst,   "",   "*xA",    "xAxB", "",   "*xA",  "xAxB",  0},
1020     {  "*xA",  "xBxA", IsFirstReconcile::kNotFirst,   "",   "*xA",    "xBxA", "",   "*xA",  "xBxA",  0},
1021     {  "*xA",  "xA",   IsFirstReconcile::kNotFirst,   "",   "*xA",    "xA",   "",   "*xA",  "xA",    0},
1022     {  "*xA",  "xB",   IsFirstReconcile::kNotFirst,   "",   "*xA",    "xB",   "",   "*xA",  "xB",    0},
1023     {  "*xAB", "B",    IsFirstReconcile::kNotFirst,   "",   "*xAB",   "B",    "",   "*xAB", "B",     0},
1024     {  "*xAB", "BxA",  IsFirstReconcile::kNotFirst,   "",   "*xAB",   "BxA",  "",   "*xAB", "BxA",   0},
1025     {  "*xAB", "xAB",  IsFirstReconcile::kNotFirst,   "",   "*xAB",   "xAB",  "",   "*xAB", "xAB",   0},
1026     {  "*xAB", "xABxC",IsFirstReconcile::kNotFirst,   "",   "*xAB",   "xABxC","",   "*xAB", "xABxC", 0},
1027     {  "A",    "AxC",  IsFirstReconcile::kNotFirst,   "",   "A",      "AxC",  "",   "A",    "AxC",   0},
1028     {  "AB",   "BxCA", IsFirstReconcile::kNotFirst,   "",   "AB",     "BxCA", "",   "AB",   "BxCA",  0},
1029     {  "B",    "xABxC",IsFirstReconcile::kNotFirst,   "",   "B",      "xABxC","",   "B",    "xABxC", 0},
1030     {  "B",    "xAxCB",IsFirstReconcile::kNotFirst,   "",   "B",      "xAxCB","",   "B",    "xAxCB", 0},
1031     {  "*ABC", "ACB",  IsFirstReconcile::kNotFirst,   "",   "*ABC",   "ACB",  "",   "*ABC", "ACB",   0},
1032     {  "*ABC", "ABC",  IsFirstReconcile::kNotFirst,   "",   "*ABC",   "ABC",  "",   "*ABC", "ABC",   0}
1033 };
1034 // clang-format on
1035 
1036 // Checks one row of the kDiceParams table above.
TEST_P(AccountReconcilorTestTable,TableRowTest)1037 TEST_P(AccountReconcilorTestTable, TableRowTest) {
1038   // Enable Dice.
1039   SetAccountConsistency(signin::AccountConsistencyMethod::kDice);
1040 
1041   // Check that reconcile is idempotent: when called twice in a row it should do
1042   // nothing on the second call.
1043   CheckReconcileIdempotent(kDiceParams, GetParam(), /*multilogin=*/false);
1044   RunReconcile();
1045   histogram_tester()->ExpectTotalCount("ForceDiceMigration.RevokeTokenAction",
1046                                        0);
1047 }
1048 
1049 INSTANTIATE_TEST_SUITE_P(
1050     DiceTable,
1051     AccountReconcilorTestTable,
1052     ::testing::ValuesIn(GenerateTestCasesFromParams(kDiceParams)));
1053 
1054 class AccountReconcilorTestForceDiceMigration
1055     : public BaseAccountReconcilorTestTable,
1056       public ::testing::WithParamInterface<ForceDiceMigrationTestTableParam> {
1057  public:
AccountReconcilorTestForceDiceMigration()1058   AccountReconcilorTestForceDiceMigration()
1059       : BaseAccountReconcilorTestTable(GetParam().tokens,
1060                                        GetParam().cookies,
1061                                        IsFirstReconcile::kFirst,
1062                                        GetParam().gaia_api_calls,
1063                                        GetParam().tokens_after_reconcile,
1064                                        GetParam().cookies_after_reconcile) {}
1065 
1066  private:
1067   DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTestForceDiceMigration);
1068 };
1069 
1070 // clang-format off
1071 const std::vector<ForceDiceMigrationTestTableParam> kForceDiceParams = {
1072     {"*A",   "AB",   "XA", "*A",    "A"   , RevokeTokenAction::kNone},
1073     {"*AxB", "AB",   "XA", "*A",    "A"   , RevokeTokenAction::kNone},
1074     {"AxB",  "AB",   "XA", "A",     "A"   , RevokeTokenAction::kNone},
1075     {"xAxB", "AB",   "X",  "",      ""    , RevokeTokenAction::kNone},
1076     {"*A",   "",     "",   "*xA",   ""    , RevokeTokenAction::kInvalidatePrimaryAccountToken},
1077     {"*A",   "B",    "X",  "*xA",   ""    , RevokeTokenAction::kInvalidatePrimaryAccountToken},
1078     {"*AB",  "B",    "",   "*xAB",  "B"   , RevokeTokenAction::kInvalidatePrimaryAccountToken},
1079     {"*AxB", "B",    "X",  "*xA",   ""    , RevokeTokenAction::kInvalidatePrimaryAccountToken},
1080     {"*ABC", "CB",   "",   "*xABC", "CB"  , RevokeTokenAction::kInvalidatePrimaryAccountToken},
1081     {"*AB",  "A",    "",   "*A",    "A"   , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1082     {"AB",   "A",    "",   "A",     "A"   , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1083     {"AB",   "",     "",   "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1084     {"xAB",  "",     "",   "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1085     {"xAB",  "A",    "X",  "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1086     {"xAB",  "xA",   "",   "",      "xA"  , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1087     {"xAB",  "B",    "",   "B",     "B"   , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1088     {"AxB",  "B",    "X",  "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1089     {"AxB",  "",     "",   "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1090     {"xAxB", "",     "",   "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1091     {"B",    "xA",   "",   "",      "xA"  , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1092     {"AB",   "xAB",  "",   "B",     "xAB" , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1093     {"xAB",  "xAC",  "X",  "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1094     {"xAB",  "AxC",  "X",  "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1095     {"AB",   "BC",   "XB", "B",     "B"   , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
1096     {"*AB",  "",     "",   "*xA",   ""    , RevokeTokenAction::kRevokeTokensForPrimaryAndSecondaryAccounts},
1097     {"*xAB", "",     "",   "*xA",   ""    , RevokeTokenAction::kRevokeTokensForPrimaryAndSecondaryAccounts},
1098     {"*AxB", "",     "",   "*xA",   ""    , RevokeTokenAction::kRevokeTokensForPrimaryAndSecondaryAccounts},
1099     {"*AB",  "xBxA", "",   "*xA",   "xBxA", RevokeTokenAction::kRevokeTokensForPrimaryAndSecondaryAccounts}
1100   };
1101 // clang-format on
1102 
1103 // Checks one row of the kForceDiceParams table above.
TEST_P(AccountReconcilorTestForceDiceMigration,TableRowTest)1104 TEST_P(AccountReconcilorTestForceDiceMigration, TableRowTest) {
1105   SetAccountConsistency(signin::AccountConsistencyMethod::kDice);
1106   EXPECT_FALSE(test_signin_client()->is_dice_migration_completed());
1107   SetDiceMigrationCompleted(false);
1108   RunReconcile();
1109   EXPECT_TRUE(test_signin_client()->is_dice_migration_completed());
1110   EXPECT_FALSE(
1111       GetMockReconcilor()->delegate_->ShouldRevokeTokensNotInCookies());
1112   histogram_tester()->ExpectUniqueSample("ForceDiceMigration.RevokeTokenAction",
1113                                          GetParam().revoke_token_action, 1);
1114 }
1115 
1116 // Check that the result state of the reconcile is in a final state (reconcile
1117 // started from this state is a no-op).
TEST_P(AccountReconcilorTestForceDiceMigration,TableRowTestCheckNoOp)1118 TEST_P(AccountReconcilorTestForceDiceMigration, TableRowTestCheckNoOp) {
1119   SetAccountConsistency(signin::AccountConsistencyMethod::kDice);
1120   // Setup cookies.
1121   std::vector<Cookie> cookies = ParseCookieString(cookies_after_reconcile_);
1122   ConfigureCookieManagerService(cookies);
1123 
1124   // Setup tokens. This triggers listing cookies so we need to setup cookies
1125   // before that.
1126   SetupTokens(tokens_after_reconcile_);
1127 
1128   // Call list accounts now so that the next call completes synchronously.
1129   identity_test_env()->identity_manager()->GetAccountsInCookieJar();
1130   base::RunLoop().RunUntilIdle();
1131 
1132   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0);
1133   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()).Times(0);
1134   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_))
1135       .Times(0);
1136 
1137   AccountReconcilor* reconcilor = GetMockReconcilor();
1138   EXPECT_FALSE(reconcilor->delegate_->ShouldRevokeTokensNotInCookies());
1139   reconcilor->StartReconcile();
1140   base::RunLoop().RunUntilIdle();
1141   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1142   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1143 }
1144 
1145 INSTANTIATE_TEST_SUITE_P(DiceMigrationTable,
1146                          AccountReconcilorTestForceDiceMigration,
1147                          ::testing::ValuesIn(kForceDiceParams));
1148 
1149 // Parameterized version of AccountReconcilorTest that tests Dice
1150 // implementation with Multilogin endpoint.
1151 class AccountReconcilorTestDiceMultilogin : public AccountReconcilorTestTable {
1152  public:
1153   AccountReconcilorTestDiceMultilogin() = default;
1154 
1155  protected:
1156   base::test::ScopedFeatureList scoped_feature_list_;
1157 
1158  private:
1159   DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTestDiceMultilogin);
1160 };
1161 
1162 // Checks one row of the kDiceParams table above.
TEST_P(AccountReconcilorTestDiceMultilogin,TableRowTest)1163 TEST_P(AccountReconcilorTestDiceMultilogin, TableRowTest) {
1164   SetAccountConsistency(signin::AccountConsistencyMethod::kDice);
1165   scoped_feature_list_.InitAndEnableFeature(kUseMultiloginEndpoint);
1166 
1167   CheckReconcileIdempotent(kDiceParams, GetParam(), /*multilogin=*/true);
1168 
1169   // Setup cookies.
1170   std::vector<Cookie> cookies = ParseCookieString(GetParam().cookies);
1171   ConfigureCookieManagerService(cookies);
1172   std::vector<Cookie> cookies_after_reconcile = cookies;
1173 
1174   // Setup tokens. This triggers listing cookies so we need to setup cookies
1175   // before that.
1176   SetupTokens(GetParam().tokens);
1177 
1178   // Call list accounts now so that the next call completes synchronously.
1179   identity_test_env()->identity_manager()->GetAccountsInCookieJar();
1180   base::RunLoop().RunUntilIdle();
1181 
1182   // Setup expectations.
1183   testing::InSequence mock_sequence;
1184   if (GetParam().gaia_api_calls_multilogin[0] != '\0') {
1185     gaia::MultiloginMode mode =
1186         GetParam().gaia_api_calls_multilogin[0] == 'U'
1187             ? gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER
1188             : gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER;
1189     // Generate expected array of accounts in cookies and set fake gaia
1190     // response.
1191     std::vector<CoreAccountId> accounts_to_send;
1192     for (int i = 1; GetParam().gaia_api_calls_multilogin[i] != '\0'; ++i) {
1193       accounts_to_send.push_back(CoreAccountId(
1194           accounts_[GetParam().gaia_api_calls_multilogin[i]].gaia_id));
1195     }
1196     const signin::MultiloginParameters params(mode, accounts_to_send);
1197     cookies_after_reconcile = FakeSetAccountsInCookie(params, cookies);
1198     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)).Times(1);
1199   }
1200   // Reconcile.
1201   AccountReconcilor* reconcilor = GetMockReconcilor();
1202   ASSERT_TRUE(reconcilor);
1203   ASSERT_TRUE(reconcilor->first_execution_);
1204   reconcilor->first_execution_ =
1205       GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false;
1206   reconcilor->StartReconcile();
1207 
1208   SimulateSetAccountsInCookieCompleted(
1209       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
1210 
1211   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1212   if (GetParam().tokens == GetParam().tokens_after_reconcile_multilogin) {
1213     EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1214   } else {
1215     // If the tokens were changed by the reconcile, a new reconcile should be
1216     // scheduled.
1217     EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED,
1218               reconcilor->GetState());
1219   }
1220   VerifyCurrentTokens(
1221       ParseTokenString(GetParam().tokens_after_reconcile_multilogin));
1222 
1223   std::vector<Cookie> cookies_after =
1224       ParseCookieString(GetParam().cookies_after_reconcile_multilogin);
1225   EXPECT_EQ(cookies_after, cookies_after_reconcile);
1226 
1227   testing::Mock::VerifyAndClearExpectations(GetMockReconcilor());
1228 
1229   // Another reconcile is sometimes triggered if Chrome accounts have
1230   // changed. Allow it to finish.
1231   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_))
1232       .WillRepeatedly(testing::Return());
1233   ConfigureCookieManagerService({});
1234   base::RunLoop().RunUntilIdle();
1235 }
1236 
1237 INSTANTIATE_TEST_SUITE_P(
1238     DiceTableMultilogin,
1239     AccountReconcilorTestDiceMultilogin,
1240     ::testing::ValuesIn(GenerateTestCasesFromParams(kDiceParams)));
1241 
1242 class AccountReconcilorDiceEndpointParamTest
1243     : public AccountReconcilorTest,
1244       public ::testing::WithParamInterface<bool> {
1245  public:
AccountReconcilorDiceEndpointParamTest()1246   AccountReconcilorDiceEndpointParamTest() {
1247     SetAccountConsistency(signin::AccountConsistencyMethod::kDice);
1248     if (IsMultiloginEnabled())
1249       scoped_feature_list_.InitAndEnableFeature(kUseMultiloginEndpoint);
1250   }
IsMultiloginEnabled()1251   bool IsMultiloginEnabled() { return GetParam(); }
1252 
1253  protected:
1254   base::test::ScopedFeatureList scoped_feature_list_;
1255 
1256  private:
1257   DISALLOW_COPY_AND_ASSIGN(AccountReconcilorDiceEndpointParamTest);
1258 };
1259 
1260 // Tests that the AccountReconcilor is always registered.
TEST_P(AccountReconcilorDiceEndpointParamTest,DiceTokenServiceRegistration)1261 TEST_P(AccountReconcilorDiceEndpointParamTest, DiceTokenServiceRegistration) {
1262   AccountReconcilor* reconcilor = GetMockReconcilor();
1263   ASSERT_TRUE(reconcilor);
1264   ASSERT_TRUE(reconcilor->IsRegisteredWithIdentityManager());
1265 
1266   identity_test_env()->MakePrimaryAccountAvailable("user@gmail.com");
1267   ASSERT_TRUE(reconcilor->IsRegisteredWithIdentityManager());
1268 
1269   // Reconcilor should not logout all accounts from the cookies when
1270   // the primary account is cleared in IdentityManager.
1271   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()).Times(0);
1272   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(::testing::_))
1273       .Times(0);
1274 
1275   identity_test_env()->ClearPrimaryAccount();
1276   ASSERT_TRUE(reconcilor->IsRegisteredWithIdentityManager());
1277 }
1278 
1279 // Tests that reconcile starts even when Sync is not enabled.
TEST_P(AccountReconcilorDiceEndpointParamTest,DiceReconcileWithoutSignin)1280 TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileWithoutSignin) {
1281   // Add a token in Chrome but do not sign in. Making account available (setting
1282   // a refresh token) triggers listing cookies so we need to setup cookies
1283   // before that.
1284   signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
1285   const CoreAccountId account_id =
1286       identity_test_env()->MakeAccountAvailable("user@gmail.com").account_id;
1287 
1288   if (!IsMultiloginEnabled()) {
1289     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id));
1290   } else {
1291     std::vector<CoreAccountId> accounts_to_send = {account_id};
1292     const signin::MultiloginParameters params(
1293         gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER,
1294         accounts_to_send);
1295     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
1296   }
1297 
1298   AccountReconcilor* reconcilor = GetMockReconcilor();
1299   reconcilor->StartReconcile();
1300 
1301   base::RunLoop().RunUntilIdle();
1302   ASSERT_TRUE(reconcilor->is_reconcile_started_);
1303   if (!IsMultiloginEnabled()) {
1304     SimulateAddAccountToCookieCompleted(
1305         reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone());
1306   } else {
1307     SimulateSetAccountsInCookieCompleted(
1308         reconcilor, signin::SetAccountsInCookieResult::kSuccess);
1309   }
1310   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1311   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1312 }
1313 
1314 // Checks that nothing happens when there is no Chrome account and no Gaia
1315 // cookie.
TEST_P(AccountReconcilorDiceEndpointParamTest,DiceReconcileNoop)1316 TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileNoop) {
1317   // No Chrome account and no cookie.
1318   signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
1319   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0);
1320   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()).Times(0);
1321   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_))
1322       .Times(0);
1323 
1324   AccountReconcilor* reconcilor = GetMockReconcilor();
1325   reconcilor->StartReconcile();
1326   ASSERT_TRUE(reconcilor->is_reconcile_started_);
1327   base::RunLoop().RunUntilIdle();
1328   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1329   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1330 }
1331 
1332 // Tests that the first Gaia account is re-used when possible.
TEST_P(AccountReconcilorDiceEndpointParamTest,DiceReconcileReuseGaiaFirstAccount)1333 TEST_P(AccountReconcilorDiceEndpointParamTest,
1334        DiceReconcileReuseGaiaFirstAccount) {
1335   // Add account "other" to the Gaia cookie.
1336   signin::SetListAccountsResponseTwoAccounts(
1337       "other@gmail.com", signin::GetTestGaiaIdForEmail("other@gmail.com"),
1338       "foo@gmail.com", "9999", &test_url_loader_factory_);
1339 
1340   // Add accounts "user" and "other" to the token service.
1341   const AccountInfo account_info_1 =
1342       identity_test_env()->MakeAccountAvailable("user@gmail.com");
1343   const CoreAccountId account_id_1 = account_info_1.account_id;
1344   const AccountInfo account_info_2 =
1345       identity_test_env()->MakeAccountAvailable("other@gmail.com");
1346   const CoreAccountId account_id_2 = account_info_2.account_id;
1347 
1348   auto* identity_manager = identity_test_env()->identity_manager();
1349   std::vector<CoreAccountInfo> accounts =
1350       identity_manager->GetAccountsWithRefreshTokens();
1351   ASSERT_EQ(2u, accounts.size());
1352   ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_1));
1353   ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_2));
1354 
1355   if (!IsMultiloginEnabled()) {
1356     testing::InSequence mock_sequence;
1357     EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
1358     // Account 2 is added first.
1359     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_2));
1360     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_1));
1361   } else {
1362     std::vector<CoreAccountId> accounts_to_send = {account_id_2, account_id_1};
1363     // Send accounts to Gaia in any order, it will determine the order itself in
1364     // PRESERVE order.
1365     const signin::MultiloginParameters params(
1366         gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER,
1367         accounts_to_send);
1368     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
1369   }
1370 
1371   AccountReconcilor* reconcilor = GetMockReconcilor();
1372   reconcilor->StartReconcile();
1373   ASSERT_TRUE(reconcilor->is_reconcile_started_);
1374   base::RunLoop().RunUntilIdle();
1375   if (!IsMultiloginEnabled()) {
1376     SimulateLogOutFromCookieCompleted(reconcilor,
1377                                       GoogleServiceAuthError::AuthErrorNone());
1378     SimulateAddAccountToCookieCompleted(
1379         reconcilor, account_id_1, GoogleServiceAuthError::AuthErrorNone());
1380     SimulateAddAccountToCookieCompleted(
1381         reconcilor, account_id_2, GoogleServiceAuthError::AuthErrorNone());
1382   } else {
1383     SimulateSetAccountsInCookieCompleted(
1384         reconcilor, signin::SetAccountsInCookieResult::kSuccess);
1385   }
1386   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1387   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1388 }
1389 
1390 // Tests that the first account is kept in cache and reused when cookies are
1391 // lost.
TEST_P(AccountReconcilorDiceEndpointParamTest,DiceLastKnownFirstAccount)1392 TEST_P(AccountReconcilorDiceEndpointParamTest, DiceLastKnownFirstAccount) {
1393   // Add accounts to the token service and the Gaia cookie in a different order.
1394   // Making account available (setting a refresh token) triggers listing cookies
1395   // so we need to setup cookies before that.
1396   signin::SetListAccountsResponseTwoAccounts(
1397       "other@gmail.com", signin::GetTestGaiaIdForEmail("other@gmail.com"),
1398       "user@gmail.com", signin::GetTestGaiaIdForEmail("user@gmail.com"),
1399       &test_url_loader_factory_);
1400 
1401   AccountInfo account_info_1 =
1402       identity_test_env()->MakeAccountAvailable("user@gmail.com");
1403   const CoreAccountId account_id_1 = account_info_1.account_id;
1404   AccountInfo account_info_2 =
1405       identity_test_env()->MakeAccountAvailable("other@gmail.com");
1406   const CoreAccountId account_id_2 = account_info_2.account_id;
1407 
1408   auto* identity_manager = identity_test_env()->identity_manager();
1409   std::vector<CoreAccountInfo> accounts =
1410       identity_manager->GetAccountsWithRefreshTokens();
1411   ASSERT_EQ(2u, accounts.size());
1412 
1413   ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_1));
1414   ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_2));
1415 
1416   // Do one reconcile. It should do nothing but to populating the last known
1417   // account.
1418   {
1419     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0);
1420     EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
1421         .Times(0);
1422     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_))
1423         .Times(0);
1424 
1425     AccountReconcilor* reconcilor = GetMockReconcilor();
1426     reconcilor->StartReconcile();
1427     ASSERT_TRUE(reconcilor->is_reconcile_started_);
1428     base::RunLoop().RunUntilIdle();
1429     ASSERT_FALSE(reconcilor->is_reconcile_started_);
1430     ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1431   }
1432 
1433   // Delete the cookies.
1434   signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
1435   identity_test_env()->SetFreshnessOfAccountsInGaiaCookie(false);
1436 
1437   if (!IsMultiloginEnabled()) {
1438     // Reconcile again and check that account_id_2 is added first.
1439     testing::InSequence mock_sequence;
1440 
1441     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_2))
1442         .Times(1);
1443     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_1))
1444         .Times(1);
1445     EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
1446         .Times(0);
1447   } else {
1448     // Since Gaia can't know about cached account, make sure that we reorder
1449     // chrome accounts accordingly even in PRESERVE mode.
1450     std::vector<CoreAccountId> accounts_to_send = {account_id_2, account_id_1};
1451     const signin::MultiloginParameters params(
1452         gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER,
1453         accounts_to_send);
1454     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
1455   }
1456 
1457   AccountReconcilor* reconcilor = GetMockReconcilor();
1458   reconcilor->StartReconcile();
1459   ASSERT_TRUE(reconcilor->is_reconcile_started_);
1460   base::RunLoop().RunUntilIdle();
1461   if (!IsMultiloginEnabled()) {
1462     SimulateAddAccountToCookieCompleted(
1463         reconcilor, account_id_2, GoogleServiceAuthError::AuthErrorNone());
1464     SimulateAddAccountToCookieCompleted(
1465         reconcilor, account_id_1, GoogleServiceAuthError::AuthErrorNone());
1466   } else {
1467     SimulateSetAccountsInCookieCompleted(
1468         reconcilor, signin::SetAccountsInCookieResult::kSuccess);
1469   }
1470   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1471   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1472 }
1473 
1474 // Checks that the reconcilor does not log out unverified accounts.
TEST_P(AccountReconcilorDiceEndpointParamTest,UnverifiedAccountNoop)1475 TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountNoop) {
1476   // Add a unverified account to the Gaia cookie.
1477   signin::SetListAccountsResponseOneAccountWithParams(
1478       {"user@gmail.com", "12345", true /* valid */, false /* signed_out */,
1479        false /* verified */},
1480       &test_url_loader_factory_);
1481 
1482   // Check that nothing happens.
1483   EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0);
1484   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()).Times(0);
1485   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_))
1486       .Times(0);
1487 
1488   AccountReconcilor* reconcilor = GetMockReconcilor();
1489   reconcilor->StartReconcile();
1490   ASSERT_TRUE(reconcilor->is_reconcile_started_);
1491   base::RunLoop().RunUntilIdle();
1492   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1493   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1494 }
1495 
1496 // Checks that the reconcilor does not log out unverified accounts when adding
1497 // a new account to the Gaia cookie.
TEST_P(AccountReconcilorDiceEndpointParamTest,UnverifiedAccountMerge)1498 TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountMerge) {
1499   // Add a unverified account to the Gaia cookie.
1500   signin::SetListAccountsResponseOneAccountWithParams(
1501       {"user@gmail.com", "12345", true /* valid */, false /* signed_out */,
1502        false /* verified */},
1503       &test_url_loader_factory_);
1504 
1505   // Add a token to Chrome.
1506   const CoreAccountId chrome_account_id =
1507       identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id;
1508 
1509   if (!IsMultiloginEnabled()) {
1510     // Check that the Chrome account is merged and the unverified account is not
1511     // logged out.
1512     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(chrome_account_id))
1513         .Times(1);
1514     EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
1515         .Times(0);
1516   } else {
1517     // In PRESERVE mode it is up to Gaia to not delete existing accounts in
1518     // cookies and not sign out unveridied accounts.
1519     std::vector<CoreAccountId> accounts_to_send = {chrome_account_id};
1520     const signin::MultiloginParameters params(
1521         gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER,
1522         accounts_to_send);
1523     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
1524   }
1525 
1526   AccountReconcilor* reconcilor = GetMockReconcilor();
1527   reconcilor->StartReconcile();
1528   ASSERT_TRUE(reconcilor->is_reconcile_started_);
1529   base::RunLoop().RunUntilIdle();
1530   if (!IsMultiloginEnabled()) {
1531     SimulateAddAccountToCookieCompleted(
1532         reconcilor, chrome_account_id, GoogleServiceAuthError::AuthErrorNone());
1533   } else {
1534     SimulateSetAccountsInCookieCompleted(
1535         reconcilor, signin::SetAccountsInCookieResult::kSuccess);
1536   }
1537   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1538   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1539 }
1540 
1541 INSTANTIATE_TEST_SUITE_P(TestDiceEndpoint,
1542                          AccountReconcilorDiceEndpointParamTest,
1543                          ::testing::ValuesIn({false, true}));
1544 
TEST_F(AccountReconcilorTest,DiceDeleteCookie)1545 TEST_F(AccountReconcilorTest, DiceDeleteCookie) {
1546   SetAccountConsistency(signin::AccountConsistencyMethod::kDice);
1547 
1548   const CoreAccountId primary_account_id =
1549       identity_test_env()
1550           ->MakePrimaryAccountAvailable("user@gmail.com")
1551           .account_id;
1552   const CoreAccountId secondary_account_id =
1553       identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id;
1554 
1555   auto* identity_manager = identity_test_env()->identity_manager();
1556   ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(primary_account_id));
1557   ASSERT_FALSE(
1558       identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
1559           primary_account_id));
1560   ASSERT_TRUE(
1561       identity_manager->HasAccountWithRefreshToken(secondary_account_id));
1562   ASSERT_FALSE(
1563       identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
1564           secondary_account_id));
1565 
1566   AccountReconcilor* reconcilor = GetMockReconcilor();
1567 
1568   // With scoped deletion, only secondary tokens are revoked.
1569   {
1570     std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> deletion =
1571         reconcilor->GetScopedSyncDataDeletion();
1572     reconcilor->OnAccountsCookieDeletedByUserAction();
1573     EXPECT_TRUE(
1574         identity_manager->HasAccountWithRefreshToken(primary_account_id));
1575     EXPECT_FALSE(
1576         identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
1577             primary_account_id));
1578     EXPECT_FALSE(
1579         identity_manager->HasAccountWithRefreshToken(secondary_account_id));
1580   }
1581 
1582   identity_test_env()->SetRefreshTokenForAccount(secondary_account_id);
1583   reconcilor->OnAccountsCookieDeletedByUserAction();
1584 
1585   // Without scoped deletion, the primary token is also invalidated.
1586   EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken(primary_account_id));
1587   EXPECT_TRUE(
1588       identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
1589           primary_account_id));
1590   EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason::
1591                 CREDENTIALS_REJECTED_BY_CLIENT,
1592             identity_manager
1593                 ->GetErrorStateOfRefreshTokenForAccount(primary_account_id)
1594                 .GetInvalidGaiaCredentialsReason());
1595   EXPECT_FALSE(
1596       identity_manager->HasAccountWithRefreshToken(secondary_account_id));
1597 
1598   // If the primary account has an error, always revoke it.
1599   identity_test_env()->SetRefreshTokenForAccount(primary_account_id);
1600   EXPECT_FALSE(
1601       identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
1602           primary_account_id));
1603   signin::UpdatePersistentErrorOfRefreshTokenForAccount(
1604       identity_test_env()->identity_manager(), primary_account_id,
1605       GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
1606           GoogleServiceAuthError::InvalidGaiaCredentialsReason::
1607               CREDENTIALS_REJECTED_BY_SERVER));
1608   {
1609     std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> deletion =
1610         reconcilor->GetScopedSyncDataDeletion();
1611     reconcilor->OnAccountsCookieDeletedByUserAction();
1612     EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason::
1613                   CREDENTIALS_REJECTED_BY_CLIENT,
1614               identity_manager
1615                   ->GetErrorStateOfRefreshTokenForAccount(primary_account_id)
1616                   .GetInvalidGaiaCredentialsReason());
1617   }
1618 }
1619 
1620 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
1621 
1622 // clang-format off
1623 const std::vector<AccountReconcilorTestTableParam> kMirrorParams = {
1624 // This table encodes the initial state and expectations of a reconcile.
1625 // See kDiceParams for documentation of the syntax.
1626 // -------------------------------------------------------------------------
1627 // Tokens | Cookies | First Run            | Gaia calls | Tokens after | Cookies after
1628 // -------------------------------------------------------------------------
1629 
1630 // First reconcile (Chrome restart): Rebuild the Gaia cookie to match the
1631 // tokens. Make the Sync account the default account in the Gaia cookie.
1632 // Sync enabled.
1633 {  "*AB",   "AB",   IsFirstReconcile::kBoth, "",          "*AB",         "AB"},
1634 {  "*AB",   "BA",   IsFirstReconcile::kBoth, "U",         "*AB",         "AB"},
1635 {  "*AB",   "A",    IsFirstReconcile::kBoth, "U",         "*AB",         "AB"},
1636 {  "*AB",   "B",    IsFirstReconcile::kBoth, "U",         "*AB",         "AB"},
1637 {  "*AB",   "",     IsFirstReconcile::kBoth, "U",         "*AB",         "AB"},
1638 // Sync enabled, token error on primary.
1639 // Sync enabled, token error on secondary.
1640 {  "*AxB",  "AB",   IsFirstReconcile::kBoth, "U",         "*AxB",        "A"},
1641 {  "*AxB",  "BA",   IsFirstReconcile::kBoth, "U",         "*AxB",        "A"},
1642 {  "*AxB",  "A",    IsFirstReconcile::kBoth, "",          "*AxB",        ""},
1643 {  "*AxB",  "B",    IsFirstReconcile::kBoth, "U",         "*AxB",        "A"},
1644 {  "*AxB",  "",     IsFirstReconcile::kBoth, "U",         "*AxB",        "A"},
1645 
1646 // Cookies can be refreshed in pace, without logout.
1647 {  "*AB",   "xBxA", IsFirstReconcile::kBoth, "U",         "*AB",         "AB"},
1648 
1649 // Check that unknown Gaia accounts are signed out.
1650 {  "*A",    "AB",   IsFirstReconcile::kBoth, "U",         "*A",          "A"},
1651 // Check that the previous case is idempotent.
1652 {  "*A",    "A",    IsFirstReconcile::kBoth, "",          "*A",          ""},
1653 };
1654 // clang-format on
1655 
1656 // Parameterized version of AccountReconcilorTest that tests Mirror
1657 // implementation with Multilogin endpoint.
1658 class AccountReconcilorTestMirrorMultilogin
1659     : public AccountReconcilorTestTable {
1660  public:
1661   AccountReconcilorTestMirrorMultilogin() = default;
1662 
1663  private:
1664   DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTestMirrorMultilogin);
1665 };
1666 
1667 // Checks one row of the kDiceParams table above.
TEST_P(AccountReconcilorTestMirrorMultilogin,TableRowTest)1668 TEST_P(AccountReconcilorTestMirrorMultilogin, TableRowTest) {
1669   // Enable Mirror.
1670   SetAccountConsistency(signin::AccountConsistencyMethod::kMirror);
1671 
1672   // Setup tokens.
1673   SetupTokens(GetParam().tokens);
1674 
1675   // Setup cookies.
1676   std::vector<Cookie> cookies = ParseCookieString(GetParam().cookies);
1677   ConfigureCookieManagerService(cookies);
1678 
1679   // Call list accounts now so that the next call completes synchronously.
1680   identity_test_env()->identity_manager()->GetAccountsInCookieJar();
1681   base::RunLoop().RunUntilIdle();
1682 
1683   // Setup expectations.
1684   testing::InSequence mock_sequence;
1685   bool logout_action = false;
1686   for (int i = 0; GetParam().gaia_api_calls[i] != '\0'; ++i) {
1687     if (GetParam().gaia_api_calls[i] == 'X') {
1688       logout_action = true;
1689       EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
1690           .Times(1);
1691       cookies.clear();
1692       continue;
1693     }
1694     if (GetParam().gaia_api_calls[i] == 'U') {
1695       std::vector<CoreAccountId> accounts_to_send;
1696       for (int i = 0; GetParam().cookies_after_reconcile[i] != '\0'; ++i) {
1697         char cookie = GetParam().cookies_after_reconcile[i];
1698         std::string account_to_send = GaiaIdForAccountKey(cookie);
1699         accounts_to_send.push_back(PickAccountIdForAccount(
1700             account_to_send,
1701             accounts_[GetParam().cookies_after_reconcile[i]].email));
1702       }
1703       const signin::MultiloginParameters ml_params(
1704           gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
1705           accounts_to_send);
1706       EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(ml_params))
1707           .Times(1);
1708     }
1709   }
1710   if (!logout_action) {
1711     EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
1712         .Times(0);
1713   }
1714 
1715   // Reconcile.
1716   AccountReconcilor* reconcilor = GetMockReconcilor();
1717   ASSERT_TRUE(reconcilor);
1718   ASSERT_TRUE(reconcilor->first_execution_);
1719   reconcilor->first_execution_ =
1720       GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false;
1721   reconcilor->StartReconcile();
1722 
1723   SimulateSetAccountsInCookieCompleted(
1724       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
1725 
1726   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1727   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1728   VerifyCurrentTokens(ParseTokenString(GetParam().tokens_after_reconcile));
1729 
1730   testing::Mock::VerifyAndClearExpectations(GetMockReconcilor());
1731 
1732   // Another reconcile is sometimes triggered if Chrome accounts have
1733   // changed. Allow it to finish.
1734   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_))
1735       .WillRepeatedly(testing::Return());
1736   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
1737       .WillRepeatedly(testing::Return());
1738   ConfigureCookieManagerService({});
1739   base::RunLoop().RunUntilIdle();
1740 }
1741 
1742 INSTANTIATE_TEST_SUITE_P(
1743     MirrorTableMultilogin,
1744     AccountReconcilorTestMirrorMultilogin,
1745     ::testing::ValuesIn(GenerateTestCasesFromParams(kMirrorParams)));
1746 
1747 #if defined(OS_CHROMEOS)
1748 class AccountReconcilorTestActiveDirectory : public AccountReconcilorTestTable {
1749  public:
1750   AccountReconcilorTestActiveDirectory() = default;
1751 
SetUp()1752   void SetUp() override {
1753     SetAccountConsistency(signin::AccountConsistencyMethod::kMirror);
1754   }
1755 
1756  private:
1757   chromeos::ScopedStubInstallAttributes install_attributes_{
1758       chromeos::StubInstallAttributes::CreateActiveDirectoryManaged(
1759           "realm.com",
1760           "device_id")};
1761 
1762   DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTestActiveDirectory);
1763 };
1764 
1765 // clang-format off
1766 const std::vector<AccountReconcilorTestTableParam> kActiveDirectoryParams = {
1767 // This table encodes the initial state and expectations of a reconcile.
1768 // See kDiceParams for documentation of the syntax.
1769 // -------------------------------------------------------------------------
1770 // Tokens  |Cookies |First Run     |Gaia calls|Tokens aft.|Cookies aft |
1771 // -------------------------------------------------------------------------
1772 {  "ABC",   "ABC",   IsFirstReconcile::kBoth,   "" ,    "ABC",   "ABC" },
1773 {  "ABC",   "",      IsFirstReconcile::kBoth,   "U",    "ABC",   "ABC" },
1774 {  "",      "ABC",   IsFirstReconcile::kBoth,   "X",    "",      "",   },
1775 // Order of Gaia accounts can be different from chrome accounts.
1776 {  "ABC",   "CBA",   IsFirstReconcile::kBoth,   "" ,    "ABC",   "CBA" },
1777 {  "ABC",   "CB",    IsFirstReconcile::kBoth,   "U",    "ABC",   "CBA" },
1778 // Gaia accounts which are not present in chrome accounts should be removed. In
1779 // this case Gaia accounts are going to be in the same order as chrome accounts.
1780 // this case Gaia accounts are going to be in thcousame order as chromcnts.
1781 {  "A",     "AB",    IsFirstReconcile::kBoth,   "U",   "A",     "A"   },
1782 {  "AB",    "CBA",   IsFirstReconcile::kBoth,   "U",   "AB",    "AB"  },
1783 {  "AB",    "C",     IsFirstReconcile::kBoth,   "U",   "AB",    "AB"  },
1784 // Cookies can be refreshed in pace, without logout.
1785 {  "AB",    "xAxB",  IsFirstReconcile::kBoth,   "U",    "AB",    "AB"  },
1786 // Token error on the account - remove it from cookies
1787 {  "AxB",   "AB",    IsFirstReconcile::kBoth,   "U",   "AxB",    "A"   },
1788 {  "xAxB",  "AB",    IsFirstReconcile::kBoth,   "X",   "xAxB",   ""    },
1789 };
1790 // clang-format on
1791 
TEST_P(AccountReconcilorTestActiveDirectory,TableRowTestMultilogin)1792 TEST_P(AccountReconcilorTestActiveDirectory, TableRowTestMultilogin) {
1793   // Setup tokens.
1794   std::vector<Token> tokens = ParseTokenString(GetParam().tokens);
1795   SetupTokens(GetParam().tokens);
1796 
1797   // Setup cookies.
1798   std::vector<Cookie> cookies = ParseCookieString(GetParam().cookies);
1799   ConfigureCookieManagerService(cookies);
1800 
1801   // Call list accounts now so that the next call completes synchronously.
1802   identity_test_env()->identity_manager()->GetAccountsInCookieJar();
1803   base::RunLoop().RunUntilIdle();
1804 
1805   testing::InSequence mock_sequence;
1806   MockAccountReconcilor* reconcilor = GetMockReconcilor(
1807       std::make_unique<signin::ActiveDirectoryAccountReconcilorDelegate>());
1808 
1809   // Setup expectations.
1810   bool logout_action = false;
1811   for (int i = 0; GetParam().gaia_api_calls[i] != '\0'; ++i) {
1812     if (GetParam().gaia_api_calls[i] == 'X') {
1813       logout_action = true;
1814       EXPECT_CALL(*reconcilor, PerformLogoutAllAccountsAction()).Times(1);
1815       cookies.clear();
1816       continue;
1817     }
1818     if (GetParam().gaia_api_calls[i] == 'U') {
1819       std::vector<CoreAccountId> accounts_to_send;
1820       for (int i = 0; GetParam().cookies_after_reconcile[i] != '\0'; ++i) {
1821         char cookie = GetParam().cookies_after_reconcile[i];
1822         std::string account_to_send = GaiaIdForAccountKey(cookie);
1823         accounts_to_send.push_back(PickAccountIdForAccount(
1824             account_to_send,
1825             accounts_[GetParam().cookies_after_reconcile[i]].email));
1826       }
1827       const signin::MultiloginParameters params(
1828           gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
1829           accounts_to_send);
1830       EXPECT_CALL(*reconcilor, PerformSetCookiesAction(params)).Times(1);
1831     }
1832   }
1833   if (!logout_action) {
1834     EXPECT_CALL(*reconcilor, PerformLogoutAllAccountsAction()).Times(0);
1835   }
1836 
1837   // Reconcile.
1838   ASSERT_TRUE(reconcilor);
1839   ASSERT_TRUE(reconcilor->first_execution_);
1840   reconcilor->first_execution_ =
1841       GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false;
1842   reconcilor->StartReconcile();
1843 
1844   SimulateSetAccountsInCookieCompleted(
1845       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
1846 
1847   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1848   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1849   VerifyCurrentTokens(ParseTokenString(GetParam().tokens_after_reconcile));
1850 
1851   testing::Mock::VerifyAndClearExpectations(reconcilor);
1852 
1853   // Another reconcile is sometimes triggered if Chrome accounts have
1854   // changed. Allow it to finish.
1855   EXPECT_CALL(*reconcilor, PerformSetCookiesAction(testing::_))
1856       .WillRepeatedly(testing::Return());
1857   EXPECT_CALL(*reconcilor, PerformLogoutAllAccountsAction())
1858       .WillRepeatedly(testing::Return());
1859   ConfigureCookieManagerService({});
1860   base::RunLoop().RunUntilIdle();
1861 }
1862 
1863 INSTANTIATE_TEST_SUITE_P(
1864     ActiveDirectoryTable,
1865     AccountReconcilorTestActiveDirectory,
1866     ::testing::ValuesIn(GenerateTestCasesFromParams(kActiveDirectoryParams)));
1867 #endif  // defined(OS_CHROMEOS)
1868 
1869 // Tests that reconcile cannot start before the tokens are loaded, and is
1870 // automatically started when tokens are loaded.
TEST_F(AccountReconcilorMirrorTest,TokensNotLoaded)1871 TEST_F(AccountReconcilorMirrorTest, TokensNotLoaded) {
1872   const CoreAccountId account_id =
1873       ConnectProfileToAccount("user@gmail.com").account_id;
1874   signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
1875   identity_test_env()->ResetToAccountsNotYetLoadedFromDiskState();
1876 
1877   AccountReconcilor* reconcilor = GetMockReconcilor();
1878   reconcilor->StartReconcile();
1879 
1880   // No reconcile when tokens are not loaded.
1881   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1882   // When tokens are loaded, reconcile starts automatically.
1883   identity_test_env()->ReloadAccountsFromDisk();
1884 
1885   std::vector<CoreAccountId> accounts_to_send = {account_id};
1886   const signin::MultiloginParameters params(
1887       gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
1888       accounts_to_send);
1889   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
1890 
1891   ASSERT_TRUE(reconcilor->is_reconcile_started_);
1892   base::RunLoop().RunUntilIdle();
1893 
1894   SimulateSetAccountsInCookieCompleted(
1895       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
1896 
1897   ASSERT_FALSE(reconcilor->is_reconcile_started_);
1898   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
1899 }
1900 
TEST_F(AccountReconcilorMirrorTest,GetAccountsFromCookieSuccess)1901 TEST_F(AccountReconcilorMirrorTest, GetAccountsFromCookieSuccess) {
1902   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
1903   const CoreAccountId account_id = account_info.account_id;
1904   signin::SetListAccountsResponseOneAccountWithParams(
1905       {account_info.email, account_info.gaia, false /* valid */,
1906        false /* signed_out */, true /* verified */},
1907       &test_url_loader_factory_);
1908 
1909   std::vector<CoreAccountId> accounts_to_send = {account_id};
1910   const signin::MultiloginParameters params(
1911       gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
1912       accounts_to_send);
1913   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
1914 
1915   AccountReconcilor* reconcilor = GetMockReconcilor();
1916   ASSERT_TRUE(reconcilor);
1917 
1918   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED,
1919             reconcilor->GetState());
1920   reconcilor->StartReconcile();
1921   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState());
1922   base::RunLoop().RunUntilIdle();
1923   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState());
1924 
1925   signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info =
1926       identity_test_env()->identity_manager()->GetAccountsInCookieJar();
1927   ASSERT_TRUE(accounts_in_cookie_jar_info.accounts_are_fresh);
1928   ASSERT_EQ(1u, accounts_in_cookie_jar_info.signed_in_accounts.size());
1929   ASSERT_EQ(account_id, accounts_in_cookie_jar_info.signed_in_accounts[0].id);
1930   ASSERT_EQ(0u, accounts_in_cookie_jar_info.signed_out_accounts.size());
1931 }
1932 
1933 // Checks that calling EnableReconcile() while the reconcilor is already running
1934 // doesn't have any effect. Regression test for https://crbug.com/1043651
TEST_F(AccountReconcilorMirrorTest,EnableReconcileWhileAlreadyRunning)1935 TEST_F(AccountReconcilorMirrorTest, EnableReconcileWhileAlreadyRunning) {
1936   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
1937   const CoreAccountId account_id = account_info.account_id;
1938   signin::SetListAccountsResponseOneAccountWithParams(
1939       {account_info.email, account_info.gaia, false /* valid */,
1940        false /* signed_out */, true /* verified */},
1941       &test_url_loader_factory_);
1942 
1943   std::vector<CoreAccountId> accounts_to_send = {account_id};
1944   const signin::MultiloginParameters params(
1945       gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
1946       accounts_to_send);
1947   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
1948 
1949   AccountReconcilor* reconcilor = GetMockReconcilor();
1950   ASSERT_TRUE(reconcilor);
1951 
1952   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED,
1953             reconcilor->GetState());
1954   reconcilor->StartReconcile();
1955   EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState());
1956   reconcilor->EnableReconcile();
1957   EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState());
1958   base::RunLoop().RunUntilIdle();
1959   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState());
1960 
1961   signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info =
1962       identity_test_env()->identity_manager()->GetAccountsInCookieJar();
1963   ASSERT_TRUE(accounts_in_cookie_jar_info.accounts_are_fresh);
1964   ASSERT_EQ(1u, accounts_in_cookie_jar_info.signed_in_accounts.size());
1965   ASSERT_EQ(account_id, accounts_in_cookie_jar_info.signed_in_accounts[0].id);
1966   ASSERT_EQ(0u, accounts_in_cookie_jar_info.signed_out_accounts.size());
1967 }
1968 
TEST_F(AccountReconcilorMirrorTest,GetAccountsFromCookieFailure)1969 TEST_F(AccountReconcilorMirrorTest, GetAccountsFromCookieFailure) {
1970   ConnectProfileToAccount("user@gmail.com");
1971   signin::SetListAccountsResponseWithUnexpectedServiceResponse(
1972       &test_url_loader_factory_);
1973 
1974   AccountReconcilor* reconcilor = GetMockReconcilor();
1975   ASSERT_TRUE(reconcilor);
1976 
1977   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED,
1978             reconcilor->GetState());
1979   reconcilor->StartReconcile();
1980   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState());
1981   base::RunLoop().RunUntilIdle();
1982 
1983   signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info =
1984       identity_test_env()->identity_manager()->GetAccountsInCookieJar();
1985   ASSERT_FALSE(accounts_in_cookie_jar_info.accounts_are_fresh);
1986   ASSERT_EQ(0u, accounts_in_cookie_jar_info.signed_in_accounts.size());
1987   ASSERT_EQ(0u, accounts_in_cookie_jar_info.signed_out_accounts.size());
1988   // List accounts retries once on |UNEXPECTED_SERVICE_RESPONSE| errors with
1989   // backoff protection.
1990   task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(2));
1991   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_ERROR, reconcilor->GetState());
1992 }
1993 
1994 // Regression test for https://crbug.com/923716
TEST_F(AccountReconcilorMirrorTest,ExtraCookieChangeNotification)1995 TEST_F(AccountReconcilorMirrorTest, ExtraCookieChangeNotification) {
1996   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
1997   const CoreAccountId account_id = account_info.account_id;
1998   signin::CookieParams cookie_params = {
1999       account_info.email, account_info.gaia, false /* valid */,
2000       false /* signed_out */, true /* verified */};
2001 
2002   signin::SetListAccountsResponseOneAccountWithParams(
2003       cookie_params, &test_url_loader_factory_);
2004 
2005   std::vector<CoreAccountId> accounts_to_send = {account_id};
2006   const signin::MultiloginParameters params(
2007       gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2008       accounts_to_send);
2009   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
2010 
2011   AccountReconcilor* reconcilor = GetMockReconcilor();
2012   ASSERT_TRUE(reconcilor);
2013 
2014   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED,
2015             reconcilor->GetState());
2016   reconcilor->StartReconcile();
2017   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState());
2018 
2019   // Add extra cookie change notification. Reconcilor should ignore it.
2020   gaia::ListedAccount listed_account =
2021       ListedAccountFromCookieParams(cookie_params, account_id);
2022   signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = {
2023       /*accounts_are_fresh=*/true, {listed_account}, {}};
2024   reconcilor->OnAccountsInCookieUpdated(
2025       accounts_in_cookie_jar_info, GoogleServiceAuthError::AuthErrorNone());
2026 
2027   base::RunLoop().RunUntilIdle();
2028 
2029   SimulateSetAccountsInCookieCompleted(
2030       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
2031 
2032   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2033   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
2034 }
2035 
TEST_F(AccountReconcilorMirrorTest,StartReconcileNoop)2036 TEST_F(AccountReconcilorMirrorTest, StartReconcileNoop) {
2037   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2038 
2039   AccountReconcilor* reconcilor = GetMockReconcilor();
2040   ASSERT_TRUE(reconcilor);
2041 
2042   signin::SetListAccountsResponseOneAccount(
2043       account_info.email, account_info.gaia, &test_url_loader_factory_);
2044 
2045   reconcilor->StartReconcile();
2046   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2047 
2048   base::RunLoop().RunUntilIdle();
2049   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2050 }
2051 
TEST_F(AccountReconcilorMirrorTest,StartReconcileCookiesDisabled)2052 TEST_F(AccountReconcilorMirrorTest, StartReconcileCookiesDisabled) {
2053   const CoreAccountId account_id =
2054       ConnectProfileToAccount("user@gmail.com").account_id;
2055   identity_test_env()->SetRefreshTokenForAccount(account_id);
2056   test_signin_client()->set_are_signin_cookies_allowed(false);
2057 
2058   AccountReconcilor* reconcilor = GetMockReconcilor();
2059   ASSERT_TRUE(reconcilor);
2060 
2061   reconcilor->StartReconcile();
2062   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2063 
2064   base::RunLoop().RunUntilIdle();
2065   std::vector<gaia::ListedAccount> accounts;
2066   // This will be the first call to ListAccounts.
2067   signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info =
2068       identity_test_env()->identity_manager()->GetAccountsInCookieJar();
2069   ASSERT_FALSE(accounts_in_cookie_jar_info.accounts_are_fresh);
2070   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2071 }
2072 
TEST_F(AccountReconcilorMirrorTest,StartReconcileContentSettings)2073 TEST_F(AccountReconcilorMirrorTest, StartReconcileContentSettings) {
2074   const CoreAccountId account_id =
2075       ConnectProfileToAccount("user@gmail.com").account_id;
2076   identity_test_env()->SetRefreshTokenForAccount(account_id);
2077 
2078   AccountReconcilor* reconcilor = GetMockReconcilor();
2079   ASSERT_TRUE(reconcilor);
2080 
2081   test_signin_client()->set_are_signin_cookies_allowed(false);
2082   SimulateCookieContentSettingsChanged(reconcilor,
2083                                        ContentSettingsPattern::Wildcard());
2084   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2085 
2086   test_signin_client()->set_are_signin_cookies_allowed(true);
2087   SimulateCookieContentSettingsChanged(reconcilor,
2088                                        ContentSettingsPattern::Wildcard());
2089   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2090 }
2091 
TEST_F(AccountReconcilorMirrorTest,StartReconcileContentSettingsGaiaUrl)2092 TEST_F(AccountReconcilorMirrorTest, StartReconcileContentSettingsGaiaUrl) {
2093   const CoreAccountId account_id =
2094       ConnectProfileToAccount("user@gmail.com").account_id;
2095   identity_test_env()->SetRefreshTokenForAccount(account_id);
2096 
2097   AccountReconcilor* reconcilor = GetMockReconcilor();
2098   ASSERT_TRUE(reconcilor);
2099 
2100   SimulateCookieContentSettingsChanged(
2101       reconcilor,
2102       ContentSettingsPattern::FromURL(GaiaUrls::GetInstance()->gaia_url()));
2103   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2104 }
2105 
TEST_F(AccountReconcilorMirrorTest,StartReconcileContentSettingsNonGaiaUrl)2106 TEST_F(AccountReconcilorMirrorTest, StartReconcileContentSettingsNonGaiaUrl) {
2107   const CoreAccountId account_id =
2108       ConnectProfileToAccount("user@gmail.com").account_id;
2109   identity_test_env()->SetRefreshTokenForAccount(account_id);
2110 
2111   AccountReconcilor* reconcilor = GetMockReconcilor();
2112   ASSERT_TRUE(reconcilor);
2113 
2114   SimulateCookieContentSettingsChanged(
2115       reconcilor,
2116       ContentSettingsPattern::FromURL(GURL("http://www.example.com")));
2117   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2118 }
2119 
TEST_F(AccountReconcilorMirrorTest,StartReconcileContentSettingsInvalidPattern)2120 TEST_F(AccountReconcilorMirrorTest,
2121        StartReconcileContentSettingsInvalidPattern) {
2122   const CoreAccountId account_id =
2123       ConnectProfileToAccount("user@gmail.com").account_id;
2124   identity_test_env()->SetRefreshTokenForAccount(account_id);
2125 
2126   AccountReconcilor* reconcilor = GetMockReconcilor();
2127   ASSERT_TRUE(reconcilor);
2128 
2129   std::unique_ptr<ContentSettingsPattern::BuilderInterface> builder =
2130       ContentSettingsPattern::CreateBuilder();
2131   builder->Invalid();
2132 
2133   SimulateCookieContentSettingsChanged(reconcilor, builder->Build());
2134   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2135 }
2136 
2137 // This test is needed until chrome changes to use gaia obfuscated id.
2138 // The primary account manager and token service use the gaia "email" property,
2139 // which preserves dots in usernames and preserves case.
2140 // gaia::ParseListAccountsData() however uses gaia "displayEmail" which does not
2141 // preserve case, and then passes the string through gaia::CanonicalizeEmail()
2142 // which removes dots.  This tests makes sure that an email like
2143 // "Dot.S@hmail.com", as seen by the token service, will be considered the same
2144 // as "dots@gmail.com" as returned by gaia::ParseListAccountsData().
TEST_F(AccountReconcilorMirrorTest,StartReconcileNoopWithDots)2145 TEST_F(AccountReconcilorMirrorTest, StartReconcileNoopWithDots) {
2146   if (identity_test_env()->identity_manager()->GetAccountIdMigrationState() !=
2147       signin::IdentityManager::AccountIdMigrationState::MIGRATION_NOT_STARTED) {
2148     return;
2149   }
2150 
2151   AccountInfo account_info = ConnectProfileToAccount("Dot.S@gmail.com");
2152   signin::SetListAccountsResponseOneAccount(
2153       account_info.email, account_info.gaia, &test_url_loader_factory_);
2154   AccountReconcilor* reconcilor = GetMockReconcilor();
2155   ASSERT_TRUE(reconcilor);
2156 
2157   reconcilor->StartReconcile();
2158   base::RunLoop().RunUntilIdle();
2159   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2160 }
2161 
TEST_F(AccountReconcilorMirrorTest,StartReconcileNoopMultiple)2162 TEST_F(AccountReconcilorMirrorTest, StartReconcileNoopMultiple) {
2163   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2164   AccountInfo account_info_2 =
2165       identity_test_env()->MakeAccountAvailable("other@gmail.com");
2166   signin::SetListAccountsResponseTwoAccounts(
2167       account_info.email, account_info.gaia, account_info_2.email,
2168       account_info_2.gaia, &test_url_loader_factory_);
2169 
2170   AccountReconcilor* reconcilor = GetMockReconcilor();
2171   ASSERT_TRUE(reconcilor);
2172 
2173   reconcilor->StartReconcile();
2174   base::RunLoop().RunUntilIdle();
2175   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2176 }
2177 
TEST_F(AccountReconcilorMirrorTest,StartReconcileAddToCookie)2178 TEST_F(AccountReconcilorMirrorTest, StartReconcileAddToCookie) {
2179   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2180   const CoreAccountId account_id = account_info.account_id;
2181   identity_test_env()->SetRefreshTokenForAccount(account_id);
2182   signin::SetListAccountsResponseOneAccount(
2183       account_info.email, account_info.gaia, &test_url_loader_factory_);
2184 
2185   const CoreAccountId account_id2 =
2186       identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id;
2187 
2188     std::vector<CoreAccountId> accounts_to_send = {account_id, account_id2};
2189     const signin::MultiloginParameters params(
2190         gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2191         accounts_to_send);
2192     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
2193 
2194   AccountReconcilor* reconcilor = GetMockReconcilor();
2195   reconcilor->StartReconcile();
2196 
2197   base::RunLoop().RunUntilIdle();
2198   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2199 
2200   SimulateSetAccountsInCookieCompleted(
2201       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
2202 
2203   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2204 
2205   base::HistogramTester::CountsMap expected_counts;
2206   expected_counts["Signin.Reconciler.Duration.UpTo3mins.Success"] = 1;
2207   EXPECT_THAT(histogram_tester()->GetTotalCountsForPrefix(
2208                   "Signin.Reconciler.Duration.UpTo3mins.Success"),
2209               testing::ContainerEq(expected_counts));
2210 }
2211 
TEST_F(AccountReconcilorTest,AuthErrorTriggersListAccount)2212 TEST_F(AccountReconcilorTest, AuthErrorTriggersListAccount) {
2213   class TestGaiaCookieObserver : public signin::IdentityManager::Observer {
2214    public:
2215     void OnAccountsInCookieUpdated(
2216         const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
2217         const GoogleServiceAuthError& error) override {
2218       cookies_updated_ = true;
2219     }
2220 
2221     bool cookies_updated_ = false;
2222   };
2223 
2224 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
2225   signin::AccountConsistencyMethod account_consistency =
2226       signin::AccountConsistencyMethod::kDice;
2227   SetAccountConsistency(account_consistency);
2228 #else
2229   signin::AccountConsistencyMethod account_consistency =
2230       signin::AccountConsistencyMethod::kMirror;
2231   SetAccountConsistency(account_consistency);
2232 #endif
2233 
2234   // Add one account to Chrome and instantiate the reconcilor.
2235   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2236   const CoreAccountId account_id = account_info.account_id;
2237   identity_test_env()->SetRefreshTokenForAccount(account_id);
2238   TestGaiaCookieObserver observer;
2239   identity_test_env()->identity_manager()->AddObserver(&observer);
2240   AccountReconcilor* reconcilor = GetMockReconcilor();
2241   base::RunLoop().RunUntilIdle();
2242   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2243   signin::SetListAccountsResponseOneAccount(
2244       account_info.email, account_info.gaia, &test_url_loader_factory_);
2245   if (account_consistency == signin::AccountConsistencyMethod::kDice) {
2246     EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction())
2247         .Times(1);
2248   }
2249 
2250   // Set an authentication error.
2251   ASSERT_FALSE(observer.cookies_updated_);
2252   signin::UpdatePersistentErrorOfRefreshTokenForAccount(
2253       identity_test_env()->identity_manager(), account_id,
2254       GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
2255           GoogleServiceAuthError::InvalidGaiaCredentialsReason::
2256               CREDENTIALS_REJECTED_BY_SERVER));
2257   base::RunLoop().RunUntilIdle();
2258 
2259   // Check that a call to ListAccount was triggered.
2260   EXPECT_TRUE(observer.cookies_updated_);
2261   testing::Mock::VerifyAndClearExpectations(GetMockReconcilor());
2262 
2263   identity_test_env()->identity_manager()->RemoveObserver(&observer);
2264 }
2265 
2266 #if !defined(OS_CHROMEOS)
2267 // This test does not run on ChromeOS because it clears the primary account,
2268 // which is not a flow that exists on ChromeOS.
2269 
TEST_F(AccountReconcilorMirrorTest,SignoutAfterErrorDoesNotRecordUma)2270 TEST_F(AccountReconcilorMirrorTest, SignoutAfterErrorDoesNotRecordUma) {
2271   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2272   const CoreAccountId account_id = account_info.account_id;
2273   identity_test_env()->SetRefreshTokenForAccount(account_id);
2274   signin::SetListAccountsResponseOneAccount(
2275       account_info.email, account_info.gaia, &test_url_loader_factory_);
2276 
2277   const CoreAccountId account_id2 =
2278       identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id;
2279 
2280     std::vector<CoreAccountId> accounts_to_send = {account_id, account_id2};
2281     const signin::MultiloginParameters params(
2282         gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2283         accounts_to_send);
2284     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
2285 
2286   AccountReconcilor* reconcilor = GetMockReconcilor();
2287   reconcilor->StartReconcile();
2288 
2289   base::RunLoop().RunUntilIdle();
2290   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2291 
2292   SimulateSetAccountsInCookieCompleted(
2293       reconcilor, signin::SetAccountsInCookieResult::kPersistentError);
2294 
2295   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2296 
2297   EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction());
2298   identity_test_env()->ClearPrimaryAccount();
2299 
2300   base::HistogramTester::CountsMap expected_counts;
2301   expected_counts["Signin.Reconciler.Duration.UpTo3mins.Failure"] = 1;
2302 }
2303 
2304 #endif  // !defined(OS_CHROMEOS)
2305 
TEST_F(AccountReconcilorMirrorTest,StartReconcileRemoveFromCookie)2306 TEST_F(AccountReconcilorMirrorTest, StartReconcileRemoveFromCookie) {
2307   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2308   const CoreAccountId account_id = account_info.account_id;
2309   identity_test_env()->SetRefreshTokenForAccount(account_id);
2310   signin::SetListAccountsResponseTwoAccounts(
2311       account_info.email, account_info.gaia, "other@gmail.com", "12345",
2312       &test_url_loader_factory_);
2313 
2314     std::vector<CoreAccountId> accounts_to_send = {account_id};
2315     const signin::MultiloginParameters params(
2316         gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2317         accounts_to_send);
2318     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
2319 
2320   AccountReconcilor* reconcilor = GetMockReconcilor();
2321   reconcilor->StartReconcile();
2322   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2323 
2324   base::RunLoop().RunUntilIdle();
2325 
2326   SimulateSetAccountsInCookieCompleted(
2327       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
2328 
2329   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2330 }
2331 
2332 // Check that reconcile is aborted if there is token error on primary account.
TEST_F(AccountReconcilorMirrorTest,TokenErrorOnPrimary)2333 TEST_F(AccountReconcilorMirrorTest, TokenErrorOnPrimary) {
2334   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2335   signin::UpdatePersistentErrorOfRefreshTokenForAccount(
2336       identity_test_env()->identity_manager(), account_info.account_id,
2337       GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
2338 
2339   signin::SetListAccountsResponseTwoAccounts(
2340       account_info.email, account_info.gaia, "other@gmail.com", "67890",
2341       &test_url_loader_factory_);
2342 
2343   AccountReconcilor* reconcilor = GetMockReconcilor();
2344   reconcilor->StartReconcile();
2345 
2346   base::RunLoop().RunUntilIdle();
2347   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2348 }
2349 
TEST_F(AccountReconcilorMirrorTest,StartReconcileAddToCookieTwice)2350 TEST_F(AccountReconcilorMirrorTest, StartReconcileAddToCookieTwice) {
2351   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2352   const CoreAccountId account_id = account_info.account_id;
2353   AccountInfo account_info2 =
2354       identity_test_env()->MakeAccountAvailable("other@gmail.com");
2355   const CoreAccountId account_id2 = account_info2.account_id;
2356 
2357   const std::string email3 = "third@gmail.com";
2358   const std::string gaia_id3 = signin::GetTestGaiaIdForEmail(email3);
2359   const CoreAccountId account_id3 = PickAccountIdForAccount(gaia_id3, email3);
2360 
2361   signin::SetListAccountsResponseOneAccount(
2362       account_info.email, account_info.gaia, &test_url_loader_factory_);
2363 
2364   std::vector<CoreAccountId> accounts_to_send_1 = {account_id, account_id2};
2365   const signin::MultiloginParameters ml_params_1(
2366       gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2367       accounts_to_send_1);
2368   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(ml_params_1));
2369 
2370   AccountReconcilor* reconcilor = GetMockReconcilor();
2371   reconcilor->StartReconcile();
2372 
2373   base::RunLoop().RunUntilIdle();
2374   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2375 
2376   SimulateSetAccountsInCookieCompleted(
2377       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
2378   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2379 
2380 
2381   // Do another pass after I've added a third account to the token service
2382   signin::SetListAccountsResponseTwoAccounts(
2383       account_info.email, account_info.gaia, account_info2.email,
2384       account_info2.gaia, &test_url_loader_factory_);
2385   identity_test_env()->SetFreshnessOfAccountsInGaiaCookie(false);
2386 
2387   // This will cause the reconcilor to fire.
2388   identity_test_env()->MakeAccountAvailable(email3);
2389   std::vector<CoreAccountId> accounts_to_send_2 = {account_id, account_id2,
2390                                                    account_id3};
2391   const signin::MultiloginParameters ml_params_2(
2392       gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2393       accounts_to_send_2);
2394   EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(ml_params_2));
2395   base::RunLoop().RunUntilIdle();
2396 
2397   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2398 
2399   SimulateSetAccountsInCookieCompleted(
2400       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
2401 
2402   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2403 }
2404 
TEST_F(AccountReconcilorMirrorTest,StartReconcileBadPrimary)2405 TEST_F(AccountReconcilorMirrorTest, StartReconcileBadPrimary) {
2406   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2407   const CoreAccountId account_id = account_info.account_id;
2408 
2409   AccountInfo account_info2 =
2410       identity_test_env()->MakeAccountAvailable("other@gmail.com");
2411   const CoreAccountId account_id2 = account_info2.account_id;
2412   signin::SetListAccountsResponseTwoAccounts(
2413       account_info2.email, account_info2.gaia, account_info.email,
2414       account_info.gaia, &test_url_loader_factory_);
2415 
2416     std::vector<CoreAccountId> accounts_to_send = {account_id, account_id2};
2417     const signin::MultiloginParameters params(
2418         gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2419         accounts_to_send);
2420     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
2421 
2422   AccountReconcilor* reconcilor = GetMockReconcilor();
2423   reconcilor->StartReconcile();
2424 
2425   base::RunLoop().RunUntilIdle();
2426   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2427 
2428   SimulateSetAccountsInCookieCompleted(
2429       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
2430 
2431   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2432 }
2433 
TEST_F(AccountReconcilorMirrorTest,StartReconcileOnlyOnce)2434 TEST_F(AccountReconcilorMirrorTest, StartReconcileOnlyOnce) {
2435   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2436   signin::SetListAccountsResponseOneAccount(
2437       account_info.email, account_info.gaia, &test_url_loader_factory_);
2438 
2439   AccountReconcilor* reconcilor = GetMockReconcilor();
2440   ASSERT_TRUE(reconcilor);
2441 
2442   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2443   reconcilor->StartReconcile();
2444   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2445 
2446   base::RunLoop().RunUntilIdle();
2447   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2448 }
2449 
TEST_F(AccountReconcilorMirrorTest,Lock)2450 TEST_F(AccountReconcilorMirrorTest, Lock) {
2451   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2452   signin::SetListAccountsResponseOneAccount(
2453       account_info.email, account_info.gaia, &test_url_loader_factory_);
2454 
2455   AccountReconcilor* reconcilor = GetMockReconcilor();
2456   ASSERT_TRUE(reconcilor);
2457   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2458   EXPECT_EQ(0, reconcilor->account_reconcilor_lock_count_);
2459 
2460   class TestAccountReconcilorObserver : public AccountReconcilor::Observer {
2461    public:
2462     void OnStateChanged(AccountReconcilorState state) override {
2463       if (state == AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING) {
2464         ++started_count_;
2465       }
2466     }
2467     void OnBlockReconcile() override { ++blocked_count_; }
2468     void OnUnblockReconcile() override { ++unblocked_count_; }
2469 
2470     int started_count_ = 0;
2471     int blocked_count_ = 0;
2472     int unblocked_count_ = 0;
2473   };
2474 
2475   TestAccountReconcilorObserver observer;
2476   ScopedObserver<AccountReconcilor, AccountReconcilor::Observer>
2477       scoped_observer(&observer);
2478   scoped_observer.Add(reconcilor);
2479 
2480   // Lock prevents reconcile from starting, as long as one instance is alive.
2481   std::unique_ptr<AccountReconcilor::Lock> lock_1 =
2482       std::make_unique<AccountReconcilor::Lock>(reconcilor);
2483   EXPECT_EQ(1, reconcilor->account_reconcilor_lock_count_);
2484   reconcilor->StartReconcile();
2485   // lock_1 is blocking the reconcile.
2486   EXPECT_FALSE(reconcilor->is_reconcile_started_);
2487   {
2488     AccountReconcilor::Lock lock_2(reconcilor);
2489     EXPECT_EQ(2, reconcilor->account_reconcilor_lock_count_);
2490     EXPECT_FALSE(reconcilor->is_reconcile_started_);
2491     lock_1.reset();
2492     // lock_1 is no longer blocking, but lock_2 is still alive.
2493     EXPECT_EQ(1, reconcilor->account_reconcilor_lock_count_);
2494     EXPECT_FALSE(reconcilor->is_reconcile_started_);
2495     EXPECT_EQ(0, observer.started_count_);
2496     EXPECT_EQ(0, observer.unblocked_count_);
2497     EXPECT_EQ(1, observer.blocked_count_);
2498   }
2499 
2500   // All locks are deleted, reconcile starts.
2501   EXPECT_EQ(0, reconcilor->account_reconcilor_lock_count_);
2502   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2503   EXPECT_EQ(1, observer.started_count_);
2504   EXPECT_EQ(1, observer.unblocked_count_);
2505   EXPECT_EQ(1, observer.blocked_count_);
2506 
2507   // Lock aborts current reconcile, and restarts it later.
2508   {
2509     AccountReconcilor::Lock lock(reconcilor);
2510     EXPECT_EQ(1, reconcilor->account_reconcilor_lock_count_);
2511     EXPECT_FALSE(reconcilor->is_reconcile_started_);
2512   }
2513   EXPECT_EQ(0, reconcilor->account_reconcilor_lock_count_);
2514   EXPECT_TRUE(reconcilor->is_reconcile_started_);
2515   EXPECT_EQ(2, observer.started_count_);
2516   EXPECT_EQ(2, observer.unblocked_count_);
2517   EXPECT_EQ(2, observer.blocked_count_);
2518 
2519   // Reconcile can complete successfully after being restarted.
2520   base::RunLoop().RunUntilIdle();
2521   EXPECT_FALSE(reconcilor->is_reconcile_started_);
2522 }
2523 
2524 // Checks that an "invalid" Gaia account can be refreshed in place, without
2525 // performing a full logout.
TEST_P(AccountReconcilorMethodParamTest,StartReconcileWithSessionInfoExpiredDefault)2526 TEST_P(AccountReconcilorMethodParamTest,
2527        StartReconcileWithSessionInfoExpiredDefault) {
2528   signin::AccountConsistencyMethod account_consistency = GetParam();
2529   SetAccountConsistency(account_consistency);
2530   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2531   const CoreAccountId account_id = account_info.account_id;
2532   AccountInfo account_info2 =
2533       identity_test_env()->MakeAccountAvailable("other@gmail.com");
2534   const CoreAccountId account_id2 = account_info2.account_id;
2535   signin::SetListAccountsResponseWithParams(
2536       {{account_info.email, account_info.gaia, false /* valid */,
2537         false /* signed_out */, true /* verified */},
2538        {account_info2.email, account_info2.gaia, true /* valid */,
2539         false /* signed_out */, true /* verified */}},
2540       &test_url_loader_factory_);
2541 
2542   AccountReconcilor* reconcilor = GetMockReconcilor();
2543   ASSERT_TRUE(reconcilor);
2544 
2545   if (!reconcilor->IsMultiloginEndpointEnabled()) {
2546     EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id));
2547   } else {
2548     switch (account_consistency) {
2549       case signin::AccountConsistencyMethod::kMirror: {
2550         signin::MultiloginParameters params(
2551             gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2552             {account_id, account_id2});
2553         EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
2554         break;
2555       }
2556       case signin::AccountConsistencyMethod::kDice: {
2557         signin::MultiloginParameters params(
2558             gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER,
2559             {account_id2, account_id});
2560         EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
2561         break;
2562       }
2563       case signin::AccountConsistencyMethod::kDisabled:
2564         NOTREACHED();
2565         break;
2566     }
2567   }
2568 
2569   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2570   reconcilor->StartReconcile();
2571   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2572 
2573   base::RunLoop().RunUntilIdle();
2574   if (!reconcilor->IsMultiloginEndpointEnabled()) {
2575     SimulateAddAccountToCookieCompleted(
2576         reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone());
2577   } else {
2578     SimulateSetAccountsInCookieCompleted(
2579         reconcilor, signin::SetAccountsInCookieResult::kSuccess);
2580   }
2581   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2582 }
2583 
TEST_F(AccountReconcilorMirrorTest,AddAccountToCookieCompletedWithBogusAccount)2584 TEST_F(AccountReconcilorMirrorTest,
2585        AddAccountToCookieCompletedWithBogusAccount) {
2586   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2587   const CoreAccountId account_id = account_info.account_id;
2588   signin::SetListAccountsResponseOneAccountWithParams(
2589       {account_info.email, account_info.gaia, false /* valid */,
2590        false /* signed_out */, true /* verified */},
2591       &test_url_loader_factory_);
2592 
2593     std::vector<CoreAccountId> accounts_to_send = {account_id};
2594     const signin::MultiloginParameters params(
2595         gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2596         accounts_to_send);
2597     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
2598 
2599   AccountReconcilor* reconcilor = GetMockReconcilor();
2600   ASSERT_TRUE(reconcilor);
2601 
2602   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2603   reconcilor->StartReconcile();
2604   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2605 
2606   base::RunLoop().RunUntilIdle();
2607 
2608   // If an unknown account id is sent, it should not upset the state.
2609   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2610 
2611   SimulateSetAccountsInCookieCompleted(
2612       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
2613 
2614   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2615 }
2616 
TEST_F(AccountReconcilorMirrorTest,NoLoopWithBadPrimary)2617 TEST_F(AccountReconcilorMirrorTest, NoLoopWithBadPrimary) {
2618   // Connect profile to a primary account and then add a secondary account.
2619   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2620   const CoreAccountId account_id1 = account_info.account_id;
2621   AccountInfo account_info2 =
2622       identity_test_env()->MakeAccountAvailable("other@gmail.com");
2623   const CoreAccountId account_id2 = account_info2.account_id;
2624 
2625     std::vector<CoreAccountId> accounts_to_send = {account_id1, account_id2};
2626     const signin::MultiloginParameters params(
2627         gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2628         accounts_to_send);
2629     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
2630 
2631   // The primary account is in auth error, so it is not in the cookie.
2632   signin::SetListAccountsResponseOneAccountWithParams(
2633       {account_info2.email, account_info2.gaia, false /* valid */,
2634        false /* signed_out */, true /* verified */},
2635       &test_url_loader_factory_);
2636 
2637   AccountReconcilor* reconcilor = GetMockReconcilor();
2638   ASSERT_TRUE(reconcilor);
2639 
2640   reconcilor->StartReconcile();
2641   base::RunLoop().RunUntilIdle();
2642   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2643 
2644   GoogleServiceAuthError error(
2645       GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
2646 
2647   // The primary cannot be added to cookie, so it fails.
2648   SimulateSetAccountsInCookieCompleted(
2649       reconcilor, signin::SetAccountsInCookieResult::kPersistentError);
2650   base::RunLoop().RunUntilIdle();
2651   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2652   ASSERT_NE(GoogleServiceAuthError::State::NONE,
2653             reconcilor->error_during_last_reconcile_.state());
2654   testing::Mock::VerifyAndClearExpectations(GetMockReconcilor());
2655 
2656   // Now that we've tried once, the token service knows that the primary
2657   // account has an auth error.
2658   signin::UpdatePersistentErrorOfRefreshTokenForAccount(
2659       identity_test_env()->identity_manager(), account_id1, error);
2660 
2661   // A second attempt to reconcile should be a noop.
2662   reconcilor->StartReconcile();
2663   base::RunLoop().RunUntilIdle();
2664   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2665   testing::Mock::VerifyAndClearExpectations(GetMockReconcilor());
2666 }
2667 
TEST_F(AccountReconcilorMirrorTest,WontMergeAccountsWithError)2668 TEST_F(AccountReconcilorMirrorTest, WontMergeAccountsWithError) {
2669   // Connect profile to a primary account and then add a secondary account.
2670   const CoreAccountId account_id1 =
2671       ConnectProfileToAccount("user@gmail.com").account_id;
2672   const CoreAccountId account_id2 =
2673       identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id;
2674 
2675   // Mark the secondary account in auth error state.
2676   signin::UpdatePersistentErrorOfRefreshTokenForAccount(
2677       identity_test_env()->identity_manager(), account_id2,
2678       GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
2679 
2680   // The cookie starts empty.
2681   signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
2682 
2683   // Since the cookie jar starts empty, the reconcilor should attempt to merge
2684   // accounts into it.  However, it should only try accounts not in auth
2685   // error state.
2686     std::vector<CoreAccountId> accounts_to_send = {account_id1};
2687     const signin::MultiloginParameters params(
2688         gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
2689         accounts_to_send);
2690     EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params));
2691 
2692   AccountReconcilor* reconcilor = GetMockReconcilor();
2693   ASSERT_TRUE(reconcilor);
2694 
2695   reconcilor->StartReconcile();
2696   base::RunLoop().RunUntilIdle();
2697   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2698 
2699   SimulateSetAccountsInCookieCompleted(
2700       reconcilor, signin::SetAccountsInCookieResult::kSuccess);
2701   base::RunLoop().RunUntilIdle();
2702   ASSERT_FALSE(reconcilor->is_reconcile_started_);
2703   ASSERT_EQ(GoogleServiceAuthError::State::NONE,
2704             reconcilor->error_during_last_reconcile_.state());
2705 }
2706 
2707 // Test that delegate timeout is called when the delegate offers a valid
2708 // timeout.
TEST_F(AccountReconcilorTest,DelegateTimeoutIsCalled)2709 TEST_F(AccountReconcilorTest, DelegateTimeoutIsCalled) {
2710   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2711   auto spy_delegate0 = std::make_unique<SpyReconcilorDelegate>();
2712   SpyReconcilorDelegate* spy_delegate = spy_delegate0.get();
2713   AccountReconcilor* reconcilor = GetMockReconcilor(std::move(spy_delegate0));
2714   ASSERT_TRUE(reconcilor);
2715   auto timer0 = std::make_unique<base::MockOneShotTimer>();
2716   base::MockOneShotTimer* timer = timer0.get();
2717   reconcilor->set_timer_for_testing(std::move(timer0));
2718 
2719   reconcilor->StartReconcile();
2720   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2721   ASSERT_TRUE(timer->IsRunning());
2722 
2723   // Simulate a timeout
2724   timer->Fire();
2725   EXPECT_EQ(1, spy_delegate->num_reconcile_timeout_calls_);
2726   EXPECT_EQ(0, spy_delegate->num_reconcile_finished_calls_);
2727   EXPECT_FALSE(reconcilor->is_reconcile_started_);
2728 }
2729 
2730 // Test that delegate timeout is not called when the delegate does not offer a
2731 // valid timeout.
TEST_F(AccountReconcilorMirrorTest,DelegateTimeoutIsNotCalled)2732 TEST_F(AccountReconcilorMirrorTest, DelegateTimeoutIsNotCalled) {
2733   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2734   signin::SetListAccountsResponseOneAccount(
2735       account_info.email, account_info.gaia, &test_url_loader_factory_);
2736   AccountReconcilor* reconcilor = GetMockReconcilor();
2737   ASSERT_TRUE(reconcilor);
2738   auto timer0 = std::make_unique<base::MockOneShotTimer>();
2739   base::MockOneShotTimer* timer = timer0.get();
2740   reconcilor->set_timer_for_testing(std::move(timer0));
2741 
2742   reconcilor->StartReconcile();
2743   EXPECT_TRUE(reconcilor->is_reconcile_started_);
2744   EXPECT_FALSE(timer->IsRunning());
2745 }
2746 
TEST_F(AccountReconcilorTest,DelegateTimeoutIsNotCalledIfTimeoutIsNotReached)2747 TEST_F(AccountReconcilorTest, DelegateTimeoutIsNotCalledIfTimeoutIsNotReached) {
2748   AccountInfo account_info = ConnectProfileToAccount("user@gmail.com");
2749   signin::SetListAccountsResponseOneAccount(
2750       account_info.email, account_info.gaia, &test_url_loader_factory_);
2751   auto spy_delegate0 = std::make_unique<SpyReconcilorDelegate>();
2752   SpyReconcilorDelegate* spy_delegate = spy_delegate0.get();
2753   AccountReconcilor* reconcilor = GetMockReconcilor(std::move(spy_delegate0));
2754   ASSERT_TRUE(reconcilor);
2755   auto timer0 = std::make_unique<base::MockOneShotTimer>();
2756   base::MockOneShotTimer* timer = timer0.get();
2757   reconcilor->set_timer_for_testing(std::move(timer0));
2758 
2759   reconcilor->StartReconcile();
2760   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2761   ASSERT_TRUE(timer->IsRunning());
2762 
2763   base::RunLoop().RunUntilIdle();
2764   EXPECT_FALSE(timer->IsRunning());
2765   EXPECT_EQ(0, spy_delegate->num_reconcile_timeout_calls_);
2766   EXPECT_EQ(1, spy_delegate->num_reconcile_finished_calls_);
2767   EXPECT_FALSE(reconcilor->is_reconcile_started_);
2768 }
2769 
TEST_F(AccountReconcilorTest,ScopedSyncedDataDeletionDestructionOrder)2770 TEST_F(AccountReconcilorTest, ScopedSyncedDataDeletionDestructionOrder) {
2771   AccountReconcilor* reconcilor = GetMockReconcilor();
2772   std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> data_deletion =
2773       reconcilor->GetScopedSyncDataDeletion();
2774   DeleteReconcilor();
2775   // data_deletion is destroyed after the reconcilor, this should not crash.
2776 }
2777 
TEST_F(AccountReconcilorTest,LockDestructionOrder)2778 TEST_F(AccountReconcilorTest, LockDestructionOrder) {
2779   AccountReconcilor* reconcilor = GetMockReconcilor();
2780   AccountReconcilor::Lock lock(reconcilor);
2781   DeleteReconcilor();
2782   // |lock| is destroyed after the reconcilor, this should not crash.
2783 }
2784 
2785 // Checks that multilogin with empty list of accounts in UPDATE mode is changed
2786 // into a Logout call.
TEST_F(AccountReconcilorTest,MultiloginLogout)2787 TEST_F(AccountReconcilorTest, MultiloginLogout) {
2788   // Delegate implementation always returning UPDATE mode with no accounts.
2789   class MultiloginLogoutDelegate : public signin::AccountReconcilorDelegate {
2790     bool IsReconcileEnabled() const override { return true; }
2791     bool IsAccountConsistencyEnforced() const override { return true; }
2792     std::vector<CoreAccountId> GetChromeAccountsForReconcile(
2793         const std::vector<CoreAccountId>& chrome_accounts,
2794         const CoreAccountId& primary_account,
2795         const std::vector<gaia::ListedAccount>& gaia_accounts,
2796         const gaia::MultiloginMode mode) const override {
2797       return {};
2798     }
2799     gaia::MultiloginMode CalculateModeForReconcile(
2800         const std::vector<gaia::ListedAccount>& gaia_accounts,
2801         const CoreAccountId& primary_account,
2802         bool first_execution,
2803         bool primary_has_error) const override {
2804       return gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER;
2805     }
2806   };
2807 
2808   MockAccountReconcilor* reconcilor =
2809       GetMockReconcilor(std::make_unique<MultiloginLogoutDelegate>());
2810   signin::SetListAccountsResponseOneAccount("user@gmail.com", "123456",
2811                                             &test_url_loader_factory_);
2812 
2813   // Logout call to Gaia.
2814   EXPECT_CALL(*reconcilor, PerformLogoutAllAccountsAction());
2815   // No multilogin call.
2816   EXPECT_CALL(*reconcilor, PerformSetCookiesAction(testing::_)).Times(0);
2817 
2818   reconcilor->StartReconcile();
2819   ASSERT_TRUE(reconcilor->is_reconcile_started_);
2820   base::RunLoop().RunUntilIdle();
2821   EXPECT_FALSE(reconcilor->is_reconcile_started_);
2822   ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState());
2823 }
2824