gcm_account_tracker_unittest.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. // Copyright 2014 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. #include "components/gcm_driver/gcm_account_tracker.h"
  5. #include <map>
  6. #include <memory>
  7. #include <string>
  8. #include <utility>
  9. #include "base/memory/raw_ptr.h"
  10. #include "base/test/task_environment.h"
  11. #include "base/time/time.h"
  12. #include "build/chromeos_buildflags.h"
  13. #include "components/gcm_driver/fake_gcm_driver.h"
  14. #include "components/signin/public/identity_manager/identity_test_environment.h"
  15. #include "google_apis/gaia/google_service_auth_error.h"
  16. #include "net/base/ip_endpoint.h"
  17. #include "net/http/http_status_code.h"
  18. #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
  19. #include "services/network/test/test_url_loader_factory.h"
  20. #include "services/network/test/test_utils.h"
  21. #include "testing/gtest/include/gtest/gtest.h"
  22. namespace gcm {
  23. namespace {
  24. const char kEmail1[] = "account_1@me.com";
  25. const char kEmail2[] = "account_2@me.com";
  26. std::string MakeAccessToken(const CoreAccountId& account_id) {
  27. return "access_token-" + account_id.ToString();
  28. }
  29. GCMClient::AccountTokenInfo MakeAccountToken(const CoreAccountInfo& account) {
  30. GCMClient::AccountTokenInfo token_info;
  31. token_info.account_id = account.account_id;
  32. // TODO(https://crbug.com/856170): This *should* be expected to be the email
  33. // address for the given account, but there is a bug in AccountTracker that
  34. // means that |token_info.email| actually gets populated with the account ID
  35. // by the production code. Hence the test expectation has to match what the
  36. // production code actually does :). If/when that bug gets fixed, this
  37. // function should be changed to take in the email address as well as the
  38. // account ID and populate this field with the email address.
  39. token_info.email = account.email;
  40. token_info.access_token = MakeAccessToken(account.account_id);
  41. return token_info;
  42. }
  43. void VerifyAccountTokens(
  44. const std::vector<GCMClient::AccountTokenInfo>& expected_tokens,
  45. const std::vector<GCMClient::AccountTokenInfo>& actual_tokens) {
  46. EXPECT_EQ(expected_tokens.size(), actual_tokens.size());
  47. for (auto expected_iter = expected_tokens.begin(),
  48. actual_iter = actual_tokens.begin();
  49. expected_iter != expected_tokens.end() &&
  50. actual_iter != actual_tokens.end();
  51. ++expected_iter, ++actual_iter) {
  52. EXPECT_EQ(expected_iter->account_id, actual_iter->account_id);
  53. EXPECT_EQ(expected_iter->email, actual_iter->email);
  54. EXPECT_EQ(expected_iter->access_token, actual_iter->access_token);
  55. }
  56. }
  57. // This version of FakeGCMDriver is customized around handling accounts and
  58. // connection events for testing GCMAccountTracker.
  59. class CustomFakeGCMDriver : public FakeGCMDriver {
  60. public:
  61. CustomFakeGCMDriver();
  62. CustomFakeGCMDriver(const CustomFakeGCMDriver&) = delete;
  63. CustomFakeGCMDriver& operator=(const CustomFakeGCMDriver&) = delete;
  64. ~CustomFakeGCMDriver() override;
  65. // GCMDriver overrides:
  66. void SetAccountTokens(
  67. const std::vector<GCMClient::AccountTokenInfo>& account_tokens) override;
  68. void AddConnectionObserver(GCMConnectionObserver* observer) override;
  69. void RemoveConnectionObserver(GCMConnectionObserver* observer) override;
  70. bool IsConnected() const override { return connected_; }
  71. base::Time GetLastTokenFetchTime() override;
  72. void SetLastTokenFetchTime(const base::Time& time) override;
  73. // Test results and helpers.
  74. void SetConnected(bool connected);
  75. void ResetResults();
  76. bool update_accounts_called() const { return update_accounts_called_; }
  77. const std::vector<GCMClient::AccountTokenInfo>& accounts() const {
  78. return accounts_;
  79. }
  80. const GCMConnectionObserver* last_connection_observer() const {
  81. return last_connection_observer_;
  82. }
  83. const GCMConnectionObserver* last_removed_connection_observer() const {
  84. return removed_connection_observer_;
  85. }
  86. private:
  87. bool connected_;
  88. std::vector<GCMClient::AccountTokenInfo> accounts_;
  89. bool update_accounts_called_;
  90. raw_ptr<GCMConnectionObserver> last_connection_observer_;
  91. raw_ptr<GCMConnectionObserver> removed_connection_observer_;
  92. net::IPEndPoint ip_endpoint_;
  93. base::Time last_token_fetch_time_;
  94. };
  95. CustomFakeGCMDriver::CustomFakeGCMDriver()
  96. : connected_(true),
  97. update_accounts_called_(false),
  98. last_connection_observer_(nullptr),
  99. removed_connection_observer_(nullptr) {}
  100. CustomFakeGCMDriver::~CustomFakeGCMDriver() {
  101. }
  102. void CustomFakeGCMDriver::SetAccountTokens(
  103. const std::vector<GCMClient::AccountTokenInfo>& accounts) {
  104. update_accounts_called_ = true;
  105. accounts_ = accounts;
  106. }
  107. void CustomFakeGCMDriver::AddConnectionObserver(
  108. GCMConnectionObserver* observer) {
  109. last_connection_observer_ = observer;
  110. }
  111. void CustomFakeGCMDriver::RemoveConnectionObserver(
  112. GCMConnectionObserver* observer) {
  113. removed_connection_observer_ = observer;
  114. }
  115. void CustomFakeGCMDriver::SetConnected(bool connected) {
  116. connected_ = connected;
  117. if (connected && last_connection_observer_)
  118. last_connection_observer_->OnConnected(ip_endpoint_);
  119. }
  120. void CustomFakeGCMDriver::ResetResults() {
  121. accounts_.clear();
  122. update_accounts_called_ = false;
  123. last_connection_observer_ = nullptr;
  124. removed_connection_observer_ = nullptr;
  125. }
  126. base::Time CustomFakeGCMDriver::GetLastTokenFetchTime() {
  127. return last_token_fetch_time_;
  128. }
  129. void CustomFakeGCMDriver::SetLastTokenFetchTime(const base::Time& time) {
  130. last_token_fetch_time_ = time;
  131. }
  132. } // namespace
  133. class GCMAccountTrackerTest : public testing::Test {
  134. public:
  135. GCMAccountTrackerTest();
  136. ~GCMAccountTrackerTest() override;
  137. // Helpers to pass fake info to the tracker.
  138. CoreAccountInfo AddAccount(const std::string& email);
  139. CoreAccountInfo SetPrimaryAccount(const std::string& email);
  140. void ClearPrimaryAccount();
  141. void RemoveAccount(const CoreAccountId& account_id);
  142. // Helpers for dealing with OAuth2 access token requests.
  143. void IssueAccessToken(const CoreAccountId& account_id);
  144. void IssueExpiredAccessToken(const CoreAccountId& account_id);
  145. void IssueError(const CoreAccountId& account_id);
  146. // Accessors to account tracker and gcm driver.
  147. GCMAccountTracker* tracker() { return tracker_.get(); }
  148. CustomFakeGCMDriver* driver() { return &driver_; }
  149. // Accessors to private methods of account tracker.
  150. bool IsFetchingRequired() const;
  151. bool IsTokenReportingRequired() const;
  152. base::TimeDelta GetTimeToNextTokenReporting() const;
  153. network::TestURLLoaderFactory* test_url_loader_factory() {
  154. return &test_url_loader_factory_;
  155. }
  156. private:
  157. CustomFakeGCMDriver driver_;
  158. base::test::SingleThreadTaskEnvironment task_environment_;
  159. network::TestURLLoaderFactory test_url_loader_factory_;
  160. signin::IdentityTestEnvironment identity_test_env_;
  161. std::unique_ptr<GCMAccountTracker> tracker_;
  162. };
  163. GCMAccountTrackerTest::GCMAccountTrackerTest() {
  164. std::unique_ptr<AccountTracker> gaia_account_tracker(
  165. new AccountTracker(identity_test_env_.identity_manager()));
  166. tracker_ = std::make_unique<GCMAccountTracker>(
  167. std::move(gaia_account_tracker), identity_test_env_.identity_manager(),
  168. &driver_);
  169. }
  170. GCMAccountTrackerTest::~GCMAccountTrackerTest() {
  171. if (tracker_)
  172. tracker_->Shutdown();
  173. }
  174. CoreAccountInfo GCMAccountTrackerTest::AddAccount(const std::string& email) {
  175. return identity_test_env_.MakeAccountAvailable(email);
  176. }
  177. CoreAccountInfo GCMAccountTrackerTest::SetPrimaryAccount(
  178. const std::string& email) {
  179. // NOTE: Setting of the primary account info must be done first on ChromeOS
  180. // to ensure that AccountTracker and GCMAccountTracker respond as expected
  181. // when the token is added to the token service.
  182. // TODO(blundell): On non-ChromeOS, it would be good to add tests wherein
  183. // setting of the primary account is done afterward to check that the flow
  184. // that ensues from the GoogleSigninSucceeded callback firing works as
  185. // expected.
  186. return identity_test_env_.MakePrimaryAccountAvailable(
  187. email, signin::ConsentLevel::kSync);
  188. }
  189. void GCMAccountTrackerTest::ClearPrimaryAccount() {
  190. identity_test_env_.ClearPrimaryAccount();
  191. }
  192. void GCMAccountTrackerTest::RemoveAccount(const CoreAccountId& account_id) {
  193. identity_test_env_.RemoveRefreshTokenForAccount(account_id);
  194. }
  195. void GCMAccountTrackerTest::IssueAccessToken(const CoreAccountId& account_id) {
  196. identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
  197. account_id, MakeAccessToken(account_id), base::Time::Max());
  198. }
  199. void GCMAccountTrackerTest::IssueExpiredAccessToken(
  200. const CoreAccountId& account_id) {
  201. identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
  202. account_id, MakeAccessToken(account_id), base::Time::Now());
  203. }
  204. void GCMAccountTrackerTest::IssueError(const CoreAccountId& account_id) {
  205. identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
  206. account_id,
  207. GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
  208. }
  209. bool GCMAccountTrackerTest::IsFetchingRequired() const {
  210. return tracker_->IsTokenFetchingRequired();
  211. }
  212. bool GCMAccountTrackerTest::IsTokenReportingRequired() const {
  213. return tracker_->IsTokenReportingRequired();
  214. }
  215. base::TimeDelta GCMAccountTrackerTest::GetTimeToNextTokenReporting() const {
  216. return tracker_->GetTimeToNextTokenReporting();
  217. }
  218. TEST_F(GCMAccountTrackerTest, NoAccounts) {
  219. EXPECT_FALSE(driver()->update_accounts_called());
  220. tracker()->Start();
  221. // Callback should not be called if there where no accounts provided.
  222. EXPECT_FALSE(driver()->update_accounts_called());
  223. EXPECT_TRUE(driver()->accounts().empty());
  224. }
  225. // Verifies that callback is called after a token is issued for a single account
  226. // with a specific scope. In this scenario, the underlying account tracker is
  227. // still working when the CompleteCollectingTokens is called for the first time.
  228. TEST_F(GCMAccountTrackerTest, SingleAccount) {
  229. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  230. tracker()->Start();
  231. EXPECT_FALSE(driver()->update_accounts_called());
  232. IssueAccessToken(account1.account_id);
  233. EXPECT_TRUE(driver()->update_accounts_called());
  234. std::vector<GCMClient::AccountTokenInfo> expected_accounts;
  235. expected_accounts.push_back(MakeAccountToken(account1));
  236. VerifyAccountTokens(expected_accounts, driver()->accounts());
  237. }
  238. TEST_F(GCMAccountTrackerTest, MultipleAccounts) {
  239. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  240. CoreAccountInfo account2 = AddAccount(kEmail2);
  241. tracker()->Start();
  242. EXPECT_FALSE(driver()->update_accounts_called());
  243. IssueAccessToken(account1.account_id);
  244. EXPECT_FALSE(driver()->update_accounts_called());
  245. IssueAccessToken(account2.account_id);
  246. EXPECT_TRUE(driver()->update_accounts_called());
  247. std::vector<GCMClient::AccountTokenInfo> expected_accounts;
  248. expected_accounts.push_back(MakeAccountToken(account1));
  249. expected_accounts.push_back(MakeAccountToken(account2));
  250. VerifyAccountTokens(expected_accounts, driver()->accounts());
  251. }
  252. TEST_F(GCMAccountTrackerTest, AccountAdded) {
  253. tracker()->Start();
  254. driver()->ResetResults();
  255. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  256. EXPECT_FALSE(driver()->update_accounts_called());
  257. IssueAccessToken(account1.account_id);
  258. EXPECT_TRUE(driver()->update_accounts_called());
  259. std::vector<GCMClient::AccountTokenInfo> expected_accounts;
  260. expected_accounts.push_back(MakeAccountToken(account1));
  261. VerifyAccountTokens(expected_accounts, driver()->accounts());
  262. }
  263. TEST_F(GCMAccountTrackerTest, AccountRemoved) {
  264. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  265. CoreAccountInfo account2 = AddAccount(kEmail2);
  266. tracker()->Start();
  267. IssueAccessToken(account1.account_id);
  268. IssueAccessToken(account2.account_id);
  269. EXPECT_TRUE(driver()->update_accounts_called());
  270. driver()->ResetResults();
  271. EXPECT_FALSE(driver()->update_accounts_called());
  272. RemoveAccount(account2.account_id);
  273. EXPECT_TRUE(driver()->update_accounts_called());
  274. std::vector<GCMClient::AccountTokenInfo> expected_accounts;
  275. expected_accounts.push_back(MakeAccountToken(account1));
  276. VerifyAccountTokens(expected_accounts, driver()->accounts());
  277. }
  278. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  279. // Tests that clearing the primary account when having multiple accounts
  280. // does not crash the application.
  281. // Regression test for crbug.com/1234406
  282. TEST_F(GCMAccountTrackerTest, AccountRemovedWithoutSyncConsentNoCrash) {
  283. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  284. CoreAccountInfo account2 = AddAccount(kEmail2);
  285. // Set last fetch time to now so that access token fetch is not required
  286. // but not started.
  287. driver()->SetLastTokenFetchTime(base::Time::Now());
  288. tracker()->Start();
  289. EXPECT_FALSE(driver()->update_accounts_called());
  290. // Reset the last fetch time to verify that clearing the primary account
  291. // will not trigger a token fetch.
  292. driver()->SetLastTokenFetchTime(base::Time());
  293. EXPECT_EQ(base::TimeDelta(), GetTimeToNextTokenReporting());
  294. ClearPrimaryAccount();
  295. EXPECT_TRUE(driver()->update_accounts_called());
  296. }
  297. #endif // !BUILDFLAG(IS_CHROMEOS_ASH)
  298. TEST_F(GCMAccountTrackerTest, GetTokenFailed) {
  299. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  300. CoreAccountInfo account2 = AddAccount(kEmail2);
  301. tracker()->Start();
  302. IssueAccessToken(account1.account_id);
  303. EXPECT_FALSE(driver()->update_accounts_called());
  304. IssueError(account2.account_id);
  305. // Failed token is not retried any more. Account marked as removed.
  306. EXPECT_EQ(0UL, tracker()->get_pending_token_request_count());
  307. EXPECT_TRUE(driver()->update_accounts_called());
  308. std::vector<GCMClient::AccountTokenInfo> expected_accounts;
  309. expected_accounts.push_back(MakeAccountToken(account1));
  310. VerifyAccountTokens(expected_accounts, driver()->accounts());
  311. }
  312. TEST_F(GCMAccountTrackerTest, GetTokenFailedAccountRemoved) {
  313. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  314. CoreAccountInfo account2 = AddAccount(kEmail2);
  315. tracker()->Start();
  316. IssueAccessToken(account1.account_id);
  317. driver()->ResetResults();
  318. RemoveAccount(account2.account_id);
  319. IssueError(account2.account_id);
  320. EXPECT_TRUE(driver()->update_accounts_called());
  321. std::vector<GCMClient::AccountTokenInfo> expected_accounts;
  322. expected_accounts.push_back(MakeAccountToken(account1));
  323. VerifyAccountTokens(expected_accounts, driver()->accounts());
  324. }
  325. TEST_F(GCMAccountTrackerTest, AccountRemovedWhileRequestsPending) {
  326. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  327. CoreAccountInfo account2 = AddAccount(kEmail2);
  328. tracker()->Start();
  329. IssueAccessToken(account1.account_id);
  330. EXPECT_FALSE(driver()->update_accounts_called());
  331. RemoveAccount(account2.account_id);
  332. IssueAccessToken(account2.account_id);
  333. EXPECT_TRUE(driver()->update_accounts_called());
  334. std::vector<GCMClient::AccountTokenInfo> expected_accounts;
  335. expected_accounts.push_back(MakeAccountToken(account1));
  336. VerifyAccountTokens(expected_accounts, driver()->accounts());
  337. }
  338. // Makes sure that tracker observes GCM connection when running.
  339. TEST_F(GCMAccountTrackerTest, TrackerObservesConnection) {
  340. EXPECT_EQ(nullptr, driver()->last_connection_observer());
  341. tracker()->Start();
  342. EXPECT_EQ(tracker(), driver()->last_connection_observer());
  343. tracker()->Shutdown();
  344. EXPECT_EQ(tracker(), driver()->last_removed_connection_observer());
  345. }
  346. // Makes sure that token fetching happens only after connection is established.
  347. TEST_F(GCMAccountTrackerTest, PostponeTokenFetchingUntilConnected) {
  348. driver()->SetConnected(false);
  349. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  350. tracker()->Start();
  351. EXPECT_EQ(0UL, tracker()->get_pending_token_request_count());
  352. driver()->SetConnected(true);
  353. EXPECT_EQ(1UL, tracker()->get_pending_token_request_count());
  354. }
  355. TEST_F(GCMAccountTrackerTest, InvalidateExpiredTokens) {
  356. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  357. CoreAccountInfo account2 = AddAccount(kEmail2);
  358. tracker()->Start();
  359. EXPECT_EQ(2UL, tracker()->get_pending_token_request_count());
  360. IssueExpiredAccessToken(account1.account_id);
  361. IssueAccessToken(account2.account_id);
  362. // Because the first token is expired, we expect the sanitize to kick in and
  363. // clean it up before the SetAccessToken is called. This also means a new
  364. // token request will be issued
  365. EXPECT_FALSE(driver()->update_accounts_called());
  366. EXPECT_EQ(1UL, tracker()->get_pending_token_request_count());
  367. }
  368. // Testing for whether there are still more tokens to be fetched. Typically the
  369. // need for token fetching triggers immediate request, unless there is no
  370. // connection, that is why connection is set on and off in this test.
  371. TEST_F(GCMAccountTrackerTest, IsTokenFetchingRequired) {
  372. tracker()->Start();
  373. driver()->SetConnected(false);
  374. EXPECT_FALSE(IsFetchingRequired());
  375. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  376. EXPECT_TRUE(IsFetchingRequired());
  377. driver()->SetConnected(true);
  378. EXPECT_FALSE(IsFetchingRequired()); // Indicates that fetching has started.
  379. IssueAccessToken(account1.account_id);
  380. EXPECT_FALSE(IsFetchingRequired());
  381. CoreAccountInfo account2 = AddAccount(kEmail2);
  382. EXPECT_FALSE(IsFetchingRequired()); // Indicates that fetching has started.
  383. // Disconnect the driver again so that the access token request being
  384. // fulfilled doesn't immediately cause another access token request (which
  385. // then would cause IsFetchingRequired() to be false, preventing us from
  386. // distinguishing this case from the case where IsFetchingRequired() is false
  387. // because GCMAccountTracker didn't detect that a new access token needs to be
  388. // fetched).
  389. driver()->SetConnected(false);
  390. IssueExpiredAccessToken(account2.account_id);
  391. // Make sure that if the token was expired it is marked as being needed again.
  392. EXPECT_TRUE(IsFetchingRequired());
  393. }
  394. // Tests what is the expected time to the next token fetching.
  395. TEST_F(GCMAccountTrackerTest, GetTimeToNextTokenReporting) {
  396. tracker()->Start();
  397. // At this point the last token fetch time is never.
  398. EXPECT_EQ(base::TimeDelta(), GetTimeToNextTokenReporting());
  399. // Regular case. The tokens have been just reported.
  400. driver()->SetLastTokenFetchTime(base::Time::Now());
  401. EXPECT_TRUE(GetTimeToNextTokenReporting() <= base::Seconds(12 * 60 * 60));
  402. // A case when gcm driver is not yet initialized.
  403. driver()->SetLastTokenFetchTime(base::Time::Max());
  404. EXPECT_EQ(base::Seconds(12 * 60 * 60), GetTimeToNextTokenReporting());
  405. // A case when token reporting calculation is expected to result in more than
  406. // 12 hours, in which case we expect exactly 12 hours.
  407. driver()->SetLastTokenFetchTime(base::Time::Now() + base::Days(2));
  408. EXPECT_EQ(base::Seconds(12 * 60 * 60), GetTimeToNextTokenReporting());
  409. }
  410. // Tests conditions when token reporting is required.
  411. TEST_F(GCMAccountTrackerTest, IsTokenReportingRequired) {
  412. tracker()->Start();
  413. // Required because it is overdue.
  414. EXPECT_TRUE(IsTokenReportingRequired());
  415. // Not required because it just happened.
  416. driver()->SetLastTokenFetchTime(base::Time::Now());
  417. EXPECT_FALSE(IsTokenReportingRequired());
  418. CoreAccountInfo account1 = SetPrimaryAccount(kEmail1);
  419. IssueAccessToken(account1.account_id);
  420. driver()->ResetResults();
  421. // Reporting was triggered, which means testing for required will give false,
  422. // but we have the update call.
  423. RemoveAccount(account1.account_id);
  424. EXPECT_TRUE(driver()->update_accounts_called());
  425. EXPECT_FALSE(IsTokenReportingRequired());
  426. }
  427. // TODO(fgorski): Add test for adding account after removal >> make sure it does
  428. // not mark removal.
  429. } // namespace gcm