diff options
Diffstat (limited to 'chromium/components/gcm_driver/account_tracker_unittest.cc')
-rw-r--r-- | chromium/components/gcm_driver/account_tracker_unittest.cc | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/chromium/components/gcm_driver/account_tracker_unittest.cc b/chromium/components/gcm_driver/account_tracker_unittest.cc new file mode 100644 index 00000000000..4901d75672b --- /dev/null +++ b/chromium/components/gcm_driver/account_tracker_unittest.cc @@ -0,0 +1,539 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/gcm_driver/account_tracker.h" + +#include <algorithm> +#include <memory> +#include <vector> + +#include "base/strings/stringprintf.h" +#include "base/test/task_environment.h" +#include "build/build_config.h" +#include "build/chromeos_buildflags.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kPrimaryAccountEmail[] = "primary_account@example.com"; + +enum TrackingEventType { SIGN_IN, SIGN_OUT }; + +class TrackingEvent { + public: + TrackingEvent(TrackingEventType type, + const CoreAccountId& account_id, + const std::string& gaia_id) + : type_(type), account_id_(account_id), gaia_id_(gaia_id) {} + + TrackingEvent(TrackingEventType type, const CoreAccountInfo& account_info) + : type_(type), + account_id_(account_info.account_id), + gaia_id_(account_info.gaia) {} + + bool operator==(const TrackingEvent& event) const { + return type_ == event.type_ && account_id_ == event.account_id_ && + gaia_id_ == event.gaia_id_; + } + + std::string ToString() const { + const char* typestr = "INVALID"; + switch (type_) { + case SIGN_IN: + typestr = " IN"; + break; + case SIGN_OUT: + typestr = "OUT"; + break; + } + return base::StringPrintf("{ type: %s, account_id: %s, gaia: %s }", typestr, + account_id_.ToString().c_str(), gaia_id_.c_str()); + } + + private: + friend bool CompareByUser(TrackingEvent a, TrackingEvent b); + + TrackingEventType type_; + CoreAccountId account_id_; + std::string gaia_id_; +}; + +bool CompareByUser(TrackingEvent a, TrackingEvent b) { + return a.account_id_ < b.account_id_; +} + +std::string Str(const std::vector<TrackingEvent>& events) { + std::string str = "["; + bool needs_comma = false; + for (auto it = events.begin(); it != events.end(); ++it) { + if (needs_comma) + str += ",\n "; + needs_comma = true; + str += it->ToString(); + } + str += "]"; + return str; +} + +} // namespace + +namespace gcm { + +class AccountTrackerObserver : public AccountTracker::Observer { + public: + AccountTrackerObserver() {} + virtual ~AccountTrackerObserver() {} + + testing::AssertionResult CheckEvents(); + testing::AssertionResult CheckEvents(const TrackingEvent& e1); + testing::AssertionResult CheckEvents(const TrackingEvent& e1, + const TrackingEvent& e2); + testing::AssertionResult CheckEvents(const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3); + testing::AssertionResult CheckEvents(const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3, + const TrackingEvent& e4); + testing::AssertionResult CheckEvents(const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3, + const TrackingEvent& e4, + const TrackingEvent& e5); + testing::AssertionResult CheckEvents(const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3, + const TrackingEvent& e4, + const TrackingEvent& e5, + const TrackingEvent& e6); + void Clear(); + void SortEventsByUser(); + + // AccountTracker::Observer implementation + void OnAccountSignInChanged(const CoreAccountInfo& account, + bool is_signed_in) override; + + private: + testing::AssertionResult CheckEvents( + const std::vector<TrackingEvent>& events); + + std::vector<TrackingEvent> events_; +}; + +void AccountTrackerObserver::OnAccountSignInChanged( + const CoreAccountInfo& account, + bool is_signed_in) { + events_.push_back(TrackingEvent(is_signed_in ? SIGN_IN : SIGN_OUT, + account.account_id, account.gaia)); +} + +void AccountTrackerObserver::Clear() { + events_.clear(); +} + +void AccountTrackerObserver::SortEventsByUser() { + std::stable_sort(events_.begin(), events_.end(), CompareByUser); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents() { + std::vector<TrackingEvent> events; + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const TrackingEvent& e1) { + std::vector<TrackingEvent> events; + events.push_back(e1); + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const TrackingEvent& e1, + const TrackingEvent& e2) { + std::vector<TrackingEvent> events; + events.push_back(e1); + events.push_back(e2); + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3) { + std::vector<TrackingEvent> events; + events.push_back(e1); + events.push_back(e2); + events.push_back(e3); + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3, + const TrackingEvent& e4) { + std::vector<TrackingEvent> events; + events.push_back(e1); + events.push_back(e2); + events.push_back(e3); + events.push_back(e4); + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3, + const TrackingEvent& e4, + const TrackingEvent& e5) { + std::vector<TrackingEvent> events; + events.push_back(e1); + events.push_back(e2); + events.push_back(e3); + events.push_back(e4); + events.push_back(e5); + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const TrackingEvent& e1, + const TrackingEvent& e2, + const TrackingEvent& e3, + const TrackingEvent& e4, + const TrackingEvent& e5, + const TrackingEvent& e6) { + std::vector<TrackingEvent> events; + events.push_back(e1); + events.push_back(e2); + events.push_back(e3); + events.push_back(e4); + events.push_back(e5); + events.push_back(e6); + return CheckEvents(events); +} + +testing::AssertionResult AccountTrackerObserver::CheckEvents( + const std::vector<TrackingEvent>& events) { + std::string maybe_newline = (events.size() + events_.size()) > 2 ? "\n" : ""; + testing::AssertionResult result( + (events_ == events) + ? testing::AssertionSuccess() + : (testing::AssertionFailure() + << "Expected " << maybe_newline << Str(events) << ", " + << maybe_newline << "Got " << maybe_newline << Str(events_))); + events_.clear(); + return result; +} + +class AccountTrackerTest : public testing::Test { + public: + AccountTrackerTest() {} + + ~AccountTrackerTest() override {} + + void SetUp() override { + account_tracker_ = + std::make_unique<AccountTracker>(identity_test_env_.identity_manager()); + account_tracker_->AddObserver(&observer_); + } + + void TearDown() override { + account_tracker_->RemoveObserver(&observer_); + account_tracker_->Shutdown(); + } + + AccountTrackerObserver* observer() { return &observer_; } + + AccountTracker* account_tracker() { return account_tracker_.get(); } + + // Helpers to pass fake events to the tracker. + + // Sets the primary account info. Returns the account ID of the + // newly-set account. + // NOTE: On ChromeOS, the login callback is never fired in production (since + // the underlying GoogleSigninSucceeded callback is never sent). Tests that + // exercise functionality dependent on that callback firing are not relevant + // on ChromeOS and should simply not run on that platform. + CoreAccountInfo SetActiveAccount(const std::string& email) { + return identity_test_env_.SetPrimaryAccount(email, + signin::ConsentLevel::kSync); + } + +// Helpers that go through a logout flow. +// NOTE: On ChromeOS, the logout callback is never fired in production (since +// the underlying GoogleSignedOut callback is never sent). Tests that exercise +// functionality dependent on that callback firing are not relevant on ChromeOS +// and should simply not run on that platform. +#if !BUILDFLAG(IS_CHROMEOS_ASH) + void NotifyLogoutOfAllAccounts() { identity_test_env_.ClearPrimaryAccount(); } +#endif + + CoreAccountInfo AddAccountWithToken(const std::string& email) { + return identity_test_env_.MakeAccountAvailable(email); + } + + void NotifyTokenAvailable(const CoreAccountId& account_id) { + identity_test_env_.SetRefreshTokenForAccount(account_id); + } + + void NotifyTokenRevoked(const CoreAccountId& account_id) { + identity_test_env_.RemoveRefreshTokenForAccount(account_id); + } + + CoreAccountInfo SetupPrimaryLogin() { + // Initial setup for tests that start with a signed in profile. + CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail); + NotifyTokenAvailable(primary_account.account_id); + observer()->Clear(); + + return primary_account; + } + + private: + // net:: stuff needs IO message loop. + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; + signin::IdentityTestEnvironment identity_test_env_; + + std::unique_ptr<AccountTracker> account_tracker_; + AccountTrackerObserver observer_; +}; + +// Primary tests just involve the Active account + +TEST_F(AccountTrackerTest, PrimaryNoEventsBeforeLogin) { + CoreAccountInfo account = AddAccountWithToken("me@dummy.com"); + NotifyTokenRevoked(account.account_id); + +// Logout is not possible on ChromeOS. +#if !BUILDFLAG(IS_CHROMEOS_ASH) + NotifyLogoutOfAllAccounts(); +#endif + + EXPECT_TRUE(observer()->CheckEvents()); +} + +TEST_F(AccountTrackerTest, PrimaryLoginThenTokenAvailable) { + CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail); + NotifyTokenAvailable(primary_account.account_id); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, primary_account))); +} + +TEST_F(AccountTrackerTest, PrimaryRevoke) { + CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail); + NotifyTokenAvailable(primary_account.account_id); + observer()->Clear(); + + NotifyTokenRevoked(primary_account.account_id); + EXPECT_TRUE( + observer()->CheckEvents(TrackingEvent(SIGN_OUT, primary_account))); +} + +TEST_F(AccountTrackerTest, PrimaryRevokeThenTokenAvailable) { + CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail); + NotifyTokenAvailable(primary_account.account_id); + NotifyTokenRevoked(primary_account.account_id); + observer()->Clear(); + + NotifyTokenAvailable(primary_account.account_id); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, primary_account))); +} + +// These tests exercise true login/logout, which are not possible on ChromeOS. +#if !BUILDFLAG(IS_CHROMEOS_ASH) +TEST_F(AccountTrackerTest, PrimaryTokenAvailableThenLogin) { + AddAccountWithToken(kPrimaryAccountEmail); + EXPECT_TRUE(observer()->CheckEvents()); + + CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, primary_account))); +} + +TEST_F(AccountTrackerTest, PrimaryTokenAvailableAndRevokedThenLogin) { + CoreAccountInfo primary_account = AddAccountWithToken(kPrimaryAccountEmail); + EXPECT_TRUE(observer()->CheckEvents()); + + NotifyTokenRevoked(primary_account.account_id); + EXPECT_TRUE(observer()->CheckEvents()); + + SetActiveAccount(kPrimaryAccountEmail); + EXPECT_TRUE(observer()->CheckEvents()); +} + +TEST_F(AccountTrackerTest, PrimaryRevokeThenLogin) { + CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail); + NotifyTokenAvailable(primary_account.account_id); + NotifyLogoutOfAllAccounts(); + observer()->Clear(); + + SetActiveAccount(kPrimaryAccountEmail); + EXPECT_TRUE(observer()->CheckEvents()); +} + +TEST_F(AccountTrackerTest, PrimaryLogoutThenRevoke) { + CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail); + NotifyTokenAvailable(primary_account.account_id); + observer()->Clear(); + + NotifyLogoutOfAllAccounts(); + EXPECT_TRUE( + observer()->CheckEvents(TrackingEvent(SIGN_OUT, primary_account))); + + NotifyTokenRevoked(primary_account.account_id); + EXPECT_TRUE(observer()->CheckEvents()); +} + +#endif + +// Non-primary accounts + +TEST_F(AccountTrackerTest, Available) { + SetupPrimaryLogin(); + + CoreAccountInfo account = AddAccountWithToken("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, account))); +} + +TEST_F(AccountTrackerTest, AvailableRevokeAvailable) { + SetupPrimaryLogin(); + + CoreAccountInfo account = AddAccountWithToken("user@example.com"); + NotifyTokenRevoked(account.account_id); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, account), + TrackingEvent(SIGN_OUT, account))); + + NotifyTokenAvailable(account.account_id); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, account))); +} + +TEST_F(AccountTrackerTest, AvailableRevokeRevoke) { + SetupPrimaryLogin(); + + CoreAccountInfo account = AddAccountWithToken("user@example.com"); + NotifyTokenRevoked(account.account_id); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, account), + TrackingEvent(SIGN_OUT, account))); + + NotifyTokenRevoked(account.account_id); + EXPECT_TRUE(observer()->CheckEvents()); +} + +TEST_F(AccountTrackerTest, AvailableAvailable) { + SetupPrimaryLogin(); + + CoreAccountInfo account = AddAccountWithToken("user@example.com"); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, account))); + + NotifyTokenAvailable(account.account_id); + EXPECT_TRUE(observer()->CheckEvents()); +} + +TEST_F(AccountTrackerTest, TwoAccounts) { + SetupPrimaryLogin(); + + CoreAccountInfo alpha_account = AddAccountWithToken("alpha@example.com"); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, alpha_account))); + + CoreAccountInfo beta_account = AddAccountWithToken("beta@example.com"); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_IN, beta_account))); + + NotifyTokenRevoked(alpha_account.account_id); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_OUT, alpha_account))); + + NotifyTokenRevoked(beta_account.account_id); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_OUT, beta_account))); +} + +// Primary/non-primary interactions + +TEST_F(AccountTrackerTest, MultiNoEventsBeforeLogin) { + CoreAccountInfo account1 = AddAccountWithToken("user@example.com"); + CoreAccountInfo account2 = AddAccountWithToken("user2@example.com"); + NotifyTokenRevoked(account2.account_id); + NotifyTokenRevoked(account2.account_id); + +// Logout is not possible on ChromeOS. +#if !BUILDFLAG(IS_CHROMEOS_ASH) + NotifyLogoutOfAllAccounts(); +#endif + + EXPECT_TRUE(observer()->CheckEvents()); +} + +TEST_F(AccountTrackerTest, MultiRevokePrimaryDoesNotRemoveAllAccounts) { + CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail); + NotifyTokenAvailable(primary_account.account_id); + CoreAccountInfo account = AddAccountWithToken("user@example.com"); + observer()->Clear(); + + NotifyTokenRevoked(primary_account.account_id); + observer()->SortEventsByUser(); + EXPECT_TRUE( + observer()->CheckEvents(TrackingEvent(SIGN_OUT, primary_account))); +} + +TEST_F(AccountTrackerTest, GetAccountsPrimary) { + CoreAccountInfo primary_account = SetupPrimaryLogin(); + + std::vector<CoreAccountInfo> account = account_tracker()->GetAccounts(); + EXPECT_EQ(1ul, account.size()); + EXPECT_EQ(primary_account.account_id, account[0].account_id); + EXPECT_EQ(primary_account.gaia, account[0].gaia); + EXPECT_EQ(primary_account.email, account[0].email); +} + +TEST_F(AccountTrackerTest, GetAccountsSignedOut) { + std::vector<CoreAccountInfo> accounts = account_tracker()->GetAccounts(); + EXPECT_EQ(0ul, accounts.size()); +} + +TEST_F(AccountTrackerTest, GetMultipleAccounts) { + CoreAccountInfo primary_account = SetupPrimaryLogin(); + CoreAccountInfo alpha_account = AddAccountWithToken("alpha@example.com"); + CoreAccountInfo beta_account = AddAccountWithToken("beta@example.com"); + + std::vector<CoreAccountInfo> account = account_tracker()->GetAccounts(); + EXPECT_EQ(3ul, account.size()); + EXPECT_EQ(primary_account.account_id, account[0].account_id); + EXPECT_EQ(primary_account.email, account[0].email); + EXPECT_EQ(primary_account.gaia, account[0].gaia); + + EXPECT_EQ(alpha_account.account_id, account[1].account_id); + EXPECT_EQ(alpha_account.email, account[1].email); + EXPECT_EQ(alpha_account.gaia, account[1].gaia); + + EXPECT_EQ(beta_account.account_id, account[2].account_id); + EXPECT_EQ(beta_account.email, account[2].email); + EXPECT_EQ(beta_account.gaia, account[2].gaia); +} + +TEST_F(AccountTrackerTest, GetAccountsReturnNothingWhenPrimarySignedOut) { + CoreAccountInfo primary_account = SetupPrimaryLogin(); + + CoreAccountInfo zeta_account = AddAccountWithToken("zeta@example.com"); + CoreAccountInfo alpha_account = AddAccountWithToken("alpha@example.com"); + + NotifyTokenRevoked(primary_account.account_id); + + std::vector<CoreAccountInfo> account = account_tracker()->GetAccounts(); + EXPECT_EQ(0ul, account.size()); +} + +// This test exercises true login/logout, which are not possible on ChromeOS. +#if !BUILDFLAG(IS_CHROMEOS_ASH) +TEST_F(AccountTrackerTest, MultiLogoutRemovesAllAccounts) { + CoreAccountInfo primary_account = SetActiveAccount(kPrimaryAccountEmail); + NotifyTokenAvailable(primary_account.account_id); + CoreAccountInfo account = AddAccountWithToken("user@example.com"); + observer()->Clear(); + + NotifyLogoutOfAllAccounts(); + observer()->SortEventsByUser(); + EXPECT_TRUE(observer()->CheckEvents(TrackingEvent(SIGN_OUT, primary_account), + TrackingEvent(SIGN_OUT, account))); +} +#endif + +} // namespace gcm |