account_tracker_unittest.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  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/account_tracker.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <vector>
  8. #include "base/strings/stringprintf.h"
  9. #include "base/test/task_environment.h"
  10. #include "build/build_config.h"
  11. #include "build/chromeos_buildflags.h"
  12. #include "components/signin/public/identity_manager/identity_test_environment.h"
  13. #include "testing/gtest/include/gtest/gtest.h"
  14. namespace {
  15. const char kPrimaryAccountEmail[] = "primary_account@example.com";
  16. enum TrackingEventType { SIGN_IN, SIGN_OUT };
  17. class TrackingEvent {
  18. public:
  19. TrackingEvent(TrackingEventType type,
  20. const CoreAccountId& account_id,
  21. const std::string& gaia_id)
  22. : type_(type), account_id_(account_id), gaia_id_(gaia_id) {}
  23. TrackingEvent(TrackingEventType type, const CoreAccountInfo& account_info)
  24. : type_(type),
  25. account_id_(account_info.account_id),
  26. gaia_id_(account_info.gaia) {}
  27. bool operator==(const TrackingEvent& event) const {
  28. return type_ == event.type_ && account_id_ == event.account_id_ &&
  29. gaia_id_ == event.gaia_id_;
  30. }
  31. std::string ToString() const {
  32. const char* typestr = "INVALID";
  33. switch (type_) {
  34. case SIGN_IN:
  35. typestr = " IN";
  36. break;
  37. case SIGN_OUT:
  38. typestr = "OUT";
  39. break;
  40. }
  41. return base::StringPrintf("{ type: %s, account_id: %s, gaia: %s }", typestr,
  42. account_id_.ToString().c_str(), gaia_id_.c_str());
  43. }
  44. private:
  45. friend bool CompareByUser(TrackingEvent a, TrackingEvent b);
  46. TrackingEventType type_;
  47. CoreAccountId account_id_;
  48. std::string gaia_id_;
  49. };
  50. bool CompareByUser(TrackingEvent a, TrackingEvent b) {
  51. return a.account_id_ < b.account_id_;
  52. }
  53. std::string Str(const std::vector<TrackingEvent>& events) {
  54. std::string str = "[";
  55. bool needs_comma = false;
  56. for (auto it = events.begin(); it != events.end(); ++it) {
  57. if (needs_comma)
  58. str += ",\n ";
  59. needs_comma = true;
  60. str += it->ToString();
  61. }
  62. str += "]";
  63. return str;
  64. }
  65. } // namespace
  66. namespace gcm {
  67. class AccountTrackerObserver : public AccountTracker::Observer {
  68. public:
  69. AccountTrackerObserver() {}
  70. virtual ~AccountTrackerObserver() {}
  71. testing::AssertionResult CheckEvents();
  72. testing::AssertionResult CheckEvents(const TrackingEvent& e1);
  73. testing::AssertionResult CheckEvents(const TrackingEvent& e1,
  74. const TrackingEvent& e2);
  75. testing::AssertionResult CheckEvents(const TrackingEvent& e1,
  76. const TrackingEvent& e2,
  77. const TrackingEvent& e3);
  78. testing::AssertionResult CheckEvents(const TrackingEvent& e1,
  79. const TrackingEvent& e2,
  80. const TrackingEvent& e3,
  81. const TrackingEvent& e4);
  82. testing::AssertionResult CheckEvents(const TrackingEvent& e1,
  83. const TrackingEvent& e2,
  84. const TrackingEvent& e3,
  85. const TrackingEvent& e4,
  86. const TrackingEvent& e5);
  87. testing::AssertionResult CheckEvents(const TrackingEvent& e1,
  88. const TrackingEvent& e2,
  89. const TrackingEvent& e3,
  90. const TrackingEvent& e4,
  91. const TrackingEvent& e5,
  92. const TrackingEvent& e6);
  93. void Clear();
  94. void SortEventsByUser();
  95. // AccountTracker::Observer implementation
  96. void OnAccountSignInChanged(const CoreAccountInfo& account,
  97. bool is_signed_in) override;
  98. private:
  99. testing::AssertionResult CheckEvents(
  100. const std::vector<TrackingEvent>& events);
  101. std::vector<TrackingEvent> events_;
  102. };
  103. void AccountTrackerObserver::OnAccountSignInChanged(
  104. const CoreAccountInfo& account,
  105. bool is_signed_in) {
  106. events_.push_back(TrackingEvent(is_signed_in ? SIGN_IN : SIGN_OUT,
  107. account.account_id, account.gaia));
  108. }
  109. void AccountTrackerObserver::Clear() {
  110. events_.clear();
  111. }
  112. void AccountTrackerObserver::SortEventsByUser() {
  113. std::stable_sort(events_.begin(), events_.end(), CompareByUser);
  114. }
  115. testing::AssertionResult AccountTrackerObserver::CheckEvents() {
  116. std::vector<TrackingEvent> events;
  117. return CheckEvents(events);
  118. }
  119. testing::AssertionResult AccountTrackerObserver::CheckEvents(
  120. const TrackingEvent& e1) {
  121. std::vector<TrackingEvent> events;
  122. events.push_back(e1);
  123. return CheckEvents(events);
  124. }
  125. testing::AssertionResult AccountTrackerObserver::CheckEvents(
  126. const TrackingEvent& e1,
  127. const TrackingEvent& e2) {
  128. std::vector<TrackingEvent> events;
  129. events.push_back(e1);
  130. events.push_back(e2);
  131. return CheckEvents(events);
  132. }
  133. testing::AssertionResult AccountTrackerObserver::CheckEvents(
  134. const TrackingEvent& e1,
  135. const TrackingEvent& e2,
  136. const TrackingEvent& e3) {
  137. std::vector<TrackingEvent> events;
  138. events.push_back(e1);
  139. events.push_back(e2);
  140. events.push_back(e3);
  141. return CheckEvents(events);
  142. }
  143. testing::AssertionResult AccountTrackerObserver::CheckEvents(
  144. const TrackingEvent& e1,
  145. const TrackingEvent& e2,
  146. const TrackingEvent& e3,
  147. const TrackingEvent& e4) {
  148. std::vector<TrackingEvent> events;
  149. events.push_back(e1);
  150. events.push_back(e2);
  151. events.push_back(e3);
  152. events.push_back(e4);
  153. return CheckEvents(events);
  154. }
  155. testing::AssertionResult AccountTrackerObserver::CheckEvents(
  156. const TrackingEvent& e1,
  157. const TrackingEvent& e2,
  158. const TrackingEvent& e3,
  159. const TrackingEvent& e4,
  160. const TrackingEvent& e5) {
  161. std::vector<TrackingEvent> events;
  162. events.push_back(e1);
  163. events.push_back(e2);
  164. events.push_back(e3);
  165. events.push_back(e4);
  166. events.push_back(e5);
  167. return CheckEvents(events);
  168. }
  169. testing::AssertionResult AccountTrackerObserver::CheckEvents(
  170. const TrackingEvent& e1,
  171. const TrackingEvent& e2,
  172. const TrackingEvent& e3,
  173. const TrackingEvent& e4,
  174. const TrackingEvent& e5,
  175. const TrackingEvent& e6) {
  176. std::vector<TrackingEvent> events;
  177. events.push_back(e1);
  178. events.push_back(e2);
  179. events.push_back(e3);
  180. events.push_back(e4);
  181. events.push_back(e5);
  182. events.push_back(e6);
  183. return CheckEvents(events);
  184. }
  185. testing::AssertionResult AccountTrackerObserver::CheckEvents(
  186. const std::vector<TrackingEvent>& events) {
  187. std::string maybe_newline = (events.size() + events_.size()) > 2 ? "\n" : "";
  188. testing::AssertionResult result(
  189. (events_ == events)
  190. ? testing::AssertionSuccess()
  191. : (testing::AssertionFailure()
  192. << "Expected " << maybe_newline << Str(events) << ", "
  193. << maybe_newline << "Got " << maybe_newline << Str(events_)));
  194. events_.clear();
  195. return result;
  196. }
  197. class AccountTrackerTest : public testing::Test {
  198. public:
  199. AccountTrackerTest() {}
  200. ~AccountTrackerTest() override {}
  201. void SetUp() override {
  202. account_tracker_ =
  203. std::make_unique<AccountTracker>(identity_test_env_.identity_manager());
  204. account_tracker_->AddObserver(&observer_);
  205. }
  206. void TearDown() override {
  207. account_tracker_->RemoveObserver(&observer_);
  208. account_tracker_->Shutdown();
  209. }
  210. AccountTrackerObserver* observer() { return &observer_; }
  211. AccountTracker* account_tracker() { return account_tracker_.get(); }
  212. // Helpers to pass fake events to the tracker.
  213. // Sets the primary account info. Returns the account ID of the
  214. // newly-set account.
  215. // NOTE: On ChromeOS, the login callback is never fired in production (since
  216. // the underlying GoogleSigninSucceeded callback is never sent). Tests that
  217. // exercise functionality dependent on that callback firing are not relevant
  218. // on ChromeOS and should simply not run on that platform.
  219. CoreAccountInfo SetActiveAccount(const std::string& email) {
  220. return identity_test_env_.SetPrimaryAccount(email,
  221. signin::ConsentLevel::kSync);
  222. }
  223. // Helpers that go through a logout flow.
  224. // NOTE: On ChromeOS, the logout callback is never fired in production (since
  225. // the underlying GoogleSignedOut callback is never sent). Tests that exercise
  226. // functionality dependent on that callback firing are not relevant on ChromeOS
  227. // and should simply not run on that platform.
  228. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  229. void NotifyLogoutOfAllAccounts() { identity_test_env_.ClearPrimaryAccount(); }
  230. #endif
  231. CoreAccountInfo AddAccountWithToken(const std::string& email) {
  232. return identity_test_env_.MakeAccountAvailable(email);
  233. }
  234. void NotifyTokenAvailable(const CoreAccountId& account_id) {
  235. identity_test_env_.SetRefreshTokenForAccount(account_id);
  236. }
  237. void NotifyTokenRevoked(const CoreAccountId& account_id) {
  238. identity_test_env_.RemoveRefreshTokenForAccount(account_id);
  239. }
  240. CoreAccountInfo SetupPrimaryLogin() {
  241. // Initial setup for tests that start with a signed in profile.
  242. CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail);
  243. NotifyTokenAvailable(primary_account.account_id);
  244. observer()->Clear();
  245. return primary_account;
  246. }
  247. private:
  248. // net:: stuff needs IO message loop.
  249. base::test::SingleThreadTaskEnvironment task_environment_{
  250. base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
  251. signin::IdentityTestEnvironment identity_test_env_;
  252. std::unique_ptr<AccountTracker> account_tracker_;
  253. AccountTrackerObserver observer_;
  254. };
  255. // Primary tests just involve the Active account
  256. TEST_F(AccountTrackerTest, PrimaryNoEventsBeforeLogin) {
  257. CoreAccountInfo account = AddAccountWithToken("me@dummy.com");
  258. NotifyTokenRevoked(account.account_id);
  259. // Logout is not possible on ChromeOS.
  260. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  261. NotifyLogoutOfAllAccounts();
  262. #endif
  263. EXPECT_TRUE(observer()->CheckEvents());
  264. }
  265. TEST_F(AccountTrackerTest, PrimaryLoginThenTokenAvailable) {
  266. CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail);
  267. NotifyTokenAvailable(primary_account.account_id);
  268. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, primary_account)));
  269. }
  270. TEST_F(AccountTrackerTest, PrimaryRevoke) {
  271. CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail);
  272. NotifyTokenAvailable(primary_account.account_id);
  273. observer()->Clear();
  274. NotifyTokenRevoked(primary_account.account_id);
  275. EXPECT_TRUE(
  276. observer()->CheckEvents(TrackingEvent(SIGN_OUT, primary_account)));
  277. }
  278. TEST_F(AccountTrackerTest, PrimaryRevokeThenTokenAvailable) {
  279. CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail);
  280. NotifyTokenAvailable(primary_account.account_id);
  281. NotifyTokenRevoked(primary_account.account_id);
  282. observer()->Clear();
  283. NotifyTokenAvailable(primary_account.account_id);
  284. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, primary_account)));
  285. }
  286. // These tests exercise true login/logout, which are not possible on ChromeOS.
  287. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  288. TEST_F(AccountTrackerTest, PrimaryTokenAvailableThenLogin) {
  289. AddAccountWithToken(kPrimaryAccountEmail);
  290. EXPECT_TRUE(observer()->CheckEvents());
  291. CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail);
  292. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, primary_account)));
  293. }
  294. TEST_F(AccountTrackerTest, PrimaryTokenAvailableAndRevokedThenLogin) {
  295. CoreAccountInfo primary_account = AddAccountWithToken(kPrimaryAccountEmail);
  296. EXPECT_TRUE(observer()->CheckEvents());
  297. NotifyTokenRevoked(primary_account.account_id);
  298. EXPECT_TRUE(observer()->CheckEvents());
  299. SetActiveAccount(kPrimaryAccountEmail);
  300. EXPECT_TRUE(observer()->CheckEvents());
  301. }
  302. TEST_F(AccountTrackerTest, PrimaryRevokeThenLogin) {
  303. CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail);
  304. NotifyTokenAvailable(primary_account.account_id);
  305. NotifyLogoutOfAllAccounts();
  306. observer()->Clear();
  307. SetActiveAccount(kPrimaryAccountEmail);
  308. EXPECT_TRUE(observer()->CheckEvents());
  309. }
  310. TEST_F(AccountTrackerTest, PrimaryLogoutThenRevoke) {
  311. CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail);
  312. NotifyTokenAvailable(primary_account.account_id);
  313. observer()->Clear();
  314. NotifyLogoutOfAllAccounts();
  315. EXPECT_TRUE(
  316. observer()->CheckEvents(TrackingEvent(SIGN_OUT, primary_account)));
  317. NotifyTokenRevoked(primary_account.account_id);
  318. EXPECT_TRUE(observer()->CheckEvents());
  319. }
  320. #endif
  321. // Non-primary accounts
  322. TEST_F(AccountTrackerTest, Available) {
  323. SetupPrimaryLogin();
  324. CoreAccountInfo account = AddAccountWithToken("user@example.com");
  325. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, account)));
  326. }
  327. TEST_F(AccountTrackerTest, AvailableRevokeAvailable) {
  328. SetupPrimaryLogin();
  329. CoreAccountInfo account = AddAccountWithToken("user@example.com");
  330. NotifyTokenRevoked(account.account_id);
  331. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, account),
  332. TrackingEvent(SIGN_OUT, account)));
  333. NotifyTokenAvailable(account.account_id);
  334. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, account)));
  335. }
  336. TEST_F(AccountTrackerTest, AvailableRevokeRevoke) {
  337. SetupPrimaryLogin();
  338. CoreAccountInfo account = AddAccountWithToken("user@example.com");
  339. NotifyTokenRevoked(account.account_id);
  340. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, account),
  341. TrackingEvent(SIGN_OUT, account)));
  342. NotifyTokenRevoked(account.account_id);
  343. EXPECT_TRUE(observer()->CheckEvents());
  344. }
  345. TEST_F(AccountTrackerTest, AvailableAvailable) {
  346. SetupPrimaryLogin();
  347. CoreAccountInfo account = AddAccountWithToken("user@example.com");
  348. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, account)));
  349. NotifyTokenAvailable(account.account_id);
  350. EXPECT_TRUE(observer()->CheckEvents());
  351. }
  352. TEST_F(AccountTrackerTest, TwoAccounts) {
  353. SetupPrimaryLogin();
  354. CoreAccountInfo alpha_account = AddAccountWithToken("alpha@example.com");
  355. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, alpha_account)));
  356. CoreAccountInfo beta_account = AddAccountWithToken("beta@example.com");
  357. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, beta_account)));
  358. NotifyTokenRevoked(alpha_account.account_id);
  359. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_OUT, alpha_account)));
  360. NotifyTokenRevoked(beta_account.account_id);
  361. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_OUT, beta_account)));
  362. }
  363. // Primary/non-primary interactions
  364. TEST_F(AccountTrackerTest, MultiNoEventsBeforeLogin) {
  365. CoreAccountInfo account1 = AddAccountWithToken("user@example.com");
  366. CoreAccountInfo account2 = AddAccountWithToken("user2@example.com");
  367. NotifyTokenRevoked(account2.account_id);
  368. NotifyTokenRevoked(account2.account_id);
  369. // Logout is not possible on ChromeOS.
  370. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  371. NotifyLogoutOfAllAccounts();
  372. #endif
  373. EXPECT_TRUE(observer()->CheckEvents());
  374. }
  375. TEST_F(AccountTrackerTest, MultiRevokePrimaryDoesNotRemoveAllAccounts) {
  376. CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail);
  377. NotifyTokenAvailable(primary_account.account_id);
  378. CoreAccountInfo account = AddAccountWithToken("user@example.com");
  379. observer()->Clear();
  380. NotifyTokenRevoked(primary_account.account_id);
  381. observer()->SortEventsByUser();
  382. EXPECT_TRUE(
  383. observer()->CheckEvents(TrackingEvent(SIGN_OUT, primary_account)));
  384. }
  385. TEST_F(AccountTrackerTest, GetAccountsPrimary) {
  386. CoreAccountInfo primary_account = SetupPrimaryLogin();
  387. std::vector<CoreAccountInfo> account = account_tracker()->GetAccounts();
  388. EXPECT_EQ(1ul, account.size());
  389. EXPECT_EQ(primary_account.account_id, account[0].account_id);
  390. EXPECT_EQ(primary_account.gaia, account[0].gaia);
  391. EXPECT_EQ(primary_account.email, account[0].email);
  392. }
  393. TEST_F(AccountTrackerTest, GetAccountsSignedOut) {
  394. std::vector<CoreAccountInfo> accounts = account_tracker()->GetAccounts();
  395. EXPECT_EQ(0ul, accounts.size());
  396. }
  397. TEST_F(AccountTrackerTest, GetMultipleAccounts) {
  398. CoreAccountInfo primary_account = SetupPrimaryLogin();
  399. CoreAccountInfo alpha_account = AddAccountWithToken("alpha@example.com");
  400. CoreAccountInfo beta_account = AddAccountWithToken("beta@example.com");
  401. std::vector<CoreAccountInfo> account = account_tracker()->GetAccounts();
  402. EXPECT_EQ(3ul, account.size());
  403. EXPECT_EQ(primary_account.account_id, account[0].account_id);
  404. EXPECT_EQ(primary_account.email, account[0].email);
  405. EXPECT_EQ(primary_account.gaia, account[0].gaia);
  406. EXPECT_EQ(alpha_account.account_id, account[1].account_id);
  407. EXPECT_EQ(alpha_account.email, account[1].email);
  408. EXPECT_EQ(alpha_account.gaia, account[1].gaia);
  409. EXPECT_EQ(beta_account.account_id, account[2].account_id);
  410. EXPECT_EQ(beta_account.email, account[2].email);
  411. EXPECT_EQ(beta_account.gaia, account[2].gaia);
  412. }
  413. TEST_F(AccountTrackerTest, GetAccountsReturnNothingWhenPrimarySignedOut) {
  414. CoreAccountInfo primary_account = SetupPrimaryLogin();
  415. CoreAccountInfo zeta_account = AddAccountWithToken("zeta@example.com");
  416. CoreAccountInfo alpha_account = AddAccountWithToken("alpha@example.com");
  417. NotifyTokenRevoked(primary_account.account_id);
  418. std::vector<CoreAccountInfo> account = account_tracker()->GetAccounts();
  419. EXPECT_EQ(0ul, account.size());
  420. }
  421. // This test exercises true login/logout, which are not possible on ChromeOS.
  422. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  423. TEST_F(AccountTrackerTest, MultiLogoutRemovesAllAccounts) {
  424. CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail);
  425. NotifyTokenAvailable(primary_account.account_id);
  426. CoreAccountInfo account = AddAccountWithToken("user@example.com");
  427. observer()->Clear();
  428. NotifyLogoutOfAllAccounts();
  429. observer()->SortEventsByUser();
  430. EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_OUT, primary_account),
  431. TrackingEvent(SIGN_OUT, account)));
  432. }
  433. #endif
  434. } // namespace gcm