blob: e64045d843df27de48ac645c6c47bade12ea4c8e [file] [log] [blame]
// Copyright 2016 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/signin/core/browser/account_investigator.h"
#include <map>
#include <string>
#include <vector>
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/timer/timer.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/signin/core/browser/signin_metrics.h"
#include "components/signin/core/browser/signin_pref_names.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "services/identity/public/cpp/identity_test_environment.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::HistogramTester;
using base::Time;
using base::TimeDelta;
using gaia::ListedAccount;
using signin_metrics::AccountRelation;
using signin_metrics::ReportingType;
class AccountInvestigatorTest : public testing::Test {
protected:
AccountInvestigatorTest()
: identity_test_env_(&test_url_loader_factory_, &prefs_),
investigator_(&prefs_, identity_test_env_.identity_manager()) {
AccountInvestigator::RegisterPrefs(prefs_.registry());
}
~AccountInvestigatorTest() override { investigator_.Shutdown(); }
identity::IdentityTestEnvironment* identity_test_env() {
return &identity_test_env_;
}
PrefService* pref_service() { return &prefs_; }
AccountInvestigator* investigator() { return &investigator_; }
// Wrappers to invoke private methods through friend class.
TimeDelta Delay(const Time previous,
const Time now,
const TimeDelta interval) {
return AccountInvestigator::CalculatePeriodicDelay(previous, now, interval);
}
std::string Hash(const std::vector<ListedAccount>& signed_in_accounts,
const std::vector<ListedAccount>& signed_out_accounts) {
return AccountInvestigator::HashAccounts(signed_in_accounts,
signed_out_accounts);
}
AccountRelation Relation(
const AccountInfo& account_info,
const std::vector<ListedAccount>& signed_in_accounts,
const std::vector<ListedAccount>& signed_out_accounts) {
return AccountInvestigator::DiscernRelation(
account_info, signed_in_accounts, signed_out_accounts);
}
void SharedReport(const std::vector<ListedAccount>& signed_in_accounts,
const std::vector<ListedAccount>& signed_out_accounts,
const Time now,
const ReportingType type) {
investigator_.SharedCookieJarReport(signed_in_accounts, signed_out_accounts,
now, type);
}
void TryPeriodicReport() { investigator_.TryPeriodicReport(); }
bool* periodic_pending() { return &investigator_.periodic_pending_; }
bool* previously_authenticated() {
return &investigator_.previously_authenticated_;
}
base::OneShotTimer* timer() { return &investigator_.timer_; }
void ExpectRelationReport(
const std::vector<ListedAccount> signed_in_accounts,
const std::vector<ListedAccount> signed_out_accounts,
const ReportingType type,
const AccountRelation expected) {
HistogramTester histogram_tester;
investigator_.SignedInAccountRelationReport(signed_in_accounts,
signed_out_accounts, type);
ExpectRelationReport(type, histogram_tester, expected);
}
void ExpectRelationReport(const ReportingType type,
const HistogramTester& histogram_tester,
const AccountRelation expected) {
histogram_tester.ExpectUniqueSample(
"Signin.CookieJar.ChromeAccountRelation" + suffix_[type],
static_cast<int>(expected), 1);
}
// If |relation| is a nullptr, then it should not have been recorded.
// If |stable_age| is a nullptr, then we're not sure what the expected time
// should have been, but it still should have been recorded.
void ExpectSharedReportHistograms(const ReportingType type,
const HistogramTester& histogram_tester,
const TimeDelta* stable_age,
const int signed_in_count,
const int signed_out_count,
const int total_count,
const AccountRelation* relation,
const bool is_shared) {
if (stable_age) {
histogram_tester.ExpectUniqueSample(
"Signin.CookieJar.StableAge" + suffix_[type], stable_age->InSeconds(),
1);
} else {
histogram_tester.ExpectTotalCount(
"Signin.CookieJar.StableAge" + suffix_[type], 1);
}
histogram_tester.ExpectUniqueSample(
"Signin.CookieJar.SignedInCount" + suffix_[type], signed_in_count, 1);
histogram_tester.ExpectUniqueSample(
"Signin.CookieJar.SignedOutCount" + suffix_[type], signed_out_count, 1);
histogram_tester.ExpectUniqueSample(
"Signin.CookieJar.TotalCount" + suffix_[type], total_count, 1);
if (relation) {
histogram_tester.ExpectUniqueSample(
"Signin.CookieJar.ChromeAccountRelation" + suffix_[type],
static_cast<int>(*relation), 1);
} else {
histogram_tester.ExpectTotalCount(
"Signin.CookieJar.ChromeAccountRelation" + suffix_[type], 0);
}
histogram_tester.ExpectUniqueSample("Signin.IsShared" + suffix_[type],
is_shared, 1);
}
private:
// Timer needs a message loop.
base::test::ScopedTaskEnvironment task_environment_;
sync_preferences::TestingPrefServiceSyncable prefs_;
network::TestURLLoaderFactory test_url_loader_factory_;
identity::IdentityTestEnvironment identity_test_env_;
AccountInvestigator investigator_;
std::map<ReportingType, std::string> suffix_ = {
{ReportingType::PERIODIC, "_Periodic"},
{ReportingType::ON_CHANGE, "_OnChange"}};
};
namespace {
ListedAccount Account(const std::string& id) {
ListedAccount account;
account.id = id;
return account;
}
AccountInfo ToAccountInfo(ListedAccount account) {
AccountInfo account_info;
account_info.account_id = account.id;
account_info.gaia = account.gaia_id;
account_info.email = account.email;
return account_info;
}
// NOTE: IdentityTestEnvironment uses a prefix for generating gaia IDs:
// "gaia_id_for_". For this reason, the tests prefix expected account IDs
// used so that there is a match.
const std::string kGaiaId1 = identity::GetTestGaiaIdForEmail("1@mail.com");
const std::string kGaiaId2 = identity::GetTestGaiaIdForEmail("2@mail.com");
const std::string kGaiaId3 = identity::GetTestGaiaIdForEmail("3@mail.com");
const ListedAccount one(Account(kGaiaId1));
const ListedAccount two(Account(kGaiaId2));
const ListedAccount three(Account(kGaiaId3));
const std::vector<ListedAccount> no_accounts{};
const std::vector<ListedAccount> just_one{one};
const std::vector<ListedAccount> just_two{two};
const std::vector<ListedAccount> both{one, two};
const std::vector<ListedAccount> both_reversed{two, one};
TEST_F(AccountInvestigatorTest, CalculatePeriodicDelay) {
const Time epoch;
const TimeDelta day(TimeDelta::FromDays(1));
const TimeDelta big(TimeDelta::FromDays(1000));
EXPECT_EQ(day, Delay(epoch, epoch, day));
EXPECT_EQ(day, Delay(epoch + big, epoch + big, day));
EXPECT_EQ(TimeDelta(), Delay(epoch, epoch + big, day));
EXPECT_EQ(day, Delay(epoch + big, epoch, day));
EXPECT_EQ(day, Delay(epoch, epoch + day, TimeDelta::FromDays(2)));
}
TEST_F(AccountInvestigatorTest, HashAccounts) {
EXPECT_EQ(Hash(no_accounts, no_accounts), Hash(no_accounts, no_accounts));
EXPECT_EQ(Hash(just_one, just_two), Hash(just_one, just_two));
EXPECT_EQ(Hash(both, no_accounts), Hash(both, no_accounts));
EXPECT_EQ(Hash(no_accounts, both), Hash(no_accounts, both));
EXPECT_EQ(Hash(both, no_accounts), Hash(both_reversed, no_accounts));
EXPECT_EQ(Hash(no_accounts, both), Hash(no_accounts, both_reversed));
EXPECT_NE(Hash(no_accounts, no_accounts), Hash(just_one, no_accounts));
EXPECT_NE(Hash(no_accounts, no_accounts), Hash(no_accounts, just_one));
EXPECT_NE(Hash(just_one, no_accounts), Hash(just_two, no_accounts));
EXPECT_NE(Hash(just_one, no_accounts), Hash(both, no_accounts));
EXPECT_NE(Hash(just_one, no_accounts), Hash(no_accounts, just_one));
}
TEST_F(AccountInvestigatorTest, DiscernRelation) {
EXPECT_EQ(AccountRelation::EMPTY_COOKIE_JAR,
Relation(ToAccountInfo(one), no_accounts, no_accounts));
EXPECT_EQ(AccountRelation::SINGLE_SIGNED_IN_MATCH_NO_SIGNED_OUT,
Relation(ToAccountInfo(one), just_one, no_accounts));
EXPECT_EQ(AccountRelation::SINGLE_SINGED_IN_MATCH_WITH_SIGNED_OUT,
Relation(ToAccountInfo(one), just_one, just_two));
EXPECT_EQ(AccountRelation::WITH_SIGNED_IN_NO_MATCH,
Relation(ToAccountInfo(one), just_two, no_accounts));
EXPECT_EQ(AccountRelation::ONE_OF_SIGNED_IN_MATCH_ANY_SIGNED_OUT,
Relation(ToAccountInfo(one), both, just_one));
EXPECT_EQ(AccountRelation::ONE_OF_SIGNED_IN_MATCH_ANY_SIGNED_OUT,
Relation(ToAccountInfo(one), both, no_accounts));
EXPECT_EQ(AccountRelation::NO_SIGNED_IN_ONE_OF_SIGNED_OUT_MATCH,
Relation(ToAccountInfo(one), no_accounts, both));
EXPECT_EQ(AccountRelation::NO_SIGNED_IN_SINGLE_SIGNED_OUT_MATCH,
Relation(ToAccountInfo(one), no_accounts, just_one));
EXPECT_EQ(AccountRelation::WITH_SIGNED_IN_ONE_OF_SIGNED_OUT_MATCH,
Relation(ToAccountInfo(one), just_two, just_one));
EXPECT_EQ(AccountRelation::NO_SIGNED_IN_WITH_SIGNED_OUT_NO_MATCH,
Relation(ToAccountInfo(three), no_accounts, both));
}
TEST_F(AccountInvestigatorTest, SignedInAccountRelationReport) {
ExpectRelationReport(just_one, no_accounts, ReportingType::PERIODIC,
AccountRelation::WITH_SIGNED_IN_NO_MATCH);
identity_test_env()->SetPrimaryAccount("1@mail.com");
ExpectRelationReport(just_one, no_accounts, ReportingType::PERIODIC,
AccountRelation::SINGLE_SIGNED_IN_MATCH_NO_SIGNED_OUT);
ExpectRelationReport(just_two, no_accounts, ReportingType::ON_CHANGE,
AccountRelation::WITH_SIGNED_IN_NO_MATCH);
}
TEST_F(AccountInvestigatorTest, SharedCookieJarReportEmpty) {
const HistogramTester histogram_tester;
const TimeDelta expected_stable_age;
SharedReport(no_accounts, no_accounts, Time(), ReportingType::PERIODIC);
ExpectSharedReportHistograms(ReportingType::PERIODIC, histogram_tester,
&expected_stable_age, 0, 0, 0, nullptr, false);
}
TEST_F(AccountInvestigatorTest, SharedCookieJarReportWithAccount) {
identity_test_env()->SetPrimaryAccount("1@mail.com");
base::Time now = base::Time::Now();
pref_service()->SetDouble(prefs::kGaiaCookieChangedTime, now.ToDoubleT());
const AccountRelation expected_relation(
AccountRelation::ONE_OF_SIGNED_IN_MATCH_ANY_SIGNED_OUT);
const HistogramTester histogram_tester;
const TimeDelta expected_stable_age(TimeDelta::FromDays(1));
SharedReport(both, no_accounts, now + TimeDelta::FromDays(1),
ReportingType::ON_CHANGE);
ExpectSharedReportHistograms(ReportingType::ON_CHANGE, histogram_tester,
&expected_stable_age, 2, 0, 2,
&expected_relation, false);
}
TEST_F(AccountInvestigatorTest, OnGaiaAccountsInCookieUpdatedError) {
const HistogramTester histogram_tester;
identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = {
/*accounts_are_fresh=*/true, just_one, no_accounts};
GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
investigator()->OnAccountsInCookieUpdated(accounts_in_cookie_jar_info, error);
EXPECT_EQ(0u, histogram_tester.GetTotalCountsForPrefix("Signin.").size());
}
TEST_F(AccountInvestigatorTest, OnGaiaAccountsInCookieUpdatedOnChange) {
const HistogramTester histogram_tester;
identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = {
/*accounts_are_fresh=*/true, just_one, no_accounts};
investigator()->OnAccountsInCookieUpdated(
accounts_in_cookie_jar_info, GoogleServiceAuthError::AuthErrorNone());
ExpectSharedReportHistograms(ReportingType::ON_CHANGE, histogram_tester,
nullptr, 1, 0, 1, nullptr, false);
}
TEST_F(AccountInvestigatorTest, OnGaiaAccountsInCookieUpdatedSigninOnly) {
// Initial update to simulate the update on first-time-run.
investigator()->OnAccountsInCookieUpdated(
identity::AccountsInCookieJarInfo(),
GoogleServiceAuthError::AuthErrorNone());
const HistogramTester histogram_tester;
identity_test_env()->SetPrimaryAccount("1@mail.com");
pref_service()->SetString(prefs::kGaiaCookieHash,
Hash(just_one, no_accounts));
identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = {
/*accounts_are_fresh=*/true, just_one, no_accounts};
investigator()->OnAccountsInCookieUpdated(
accounts_in_cookie_jar_info, GoogleServiceAuthError::AuthErrorNone());
EXPECT_EQ(1u, histogram_tester.GetTotalCountsForPrefix("Signin.").size());
ExpectRelationReport(ReportingType::ON_CHANGE, histogram_tester,
AccountRelation::SINGLE_SIGNED_IN_MATCH_NO_SIGNED_OUT);
}
TEST_F(AccountInvestigatorTest,
OnGaiaAccountsInCookieUpdatedSigninSignOutOfContent) {
const HistogramTester histogram_tester;
identity_test_env()->SetPrimaryAccount("1@mail.com");
identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = {
/*accounts_are_fresh=*/true, just_one, no_accounts};
investigator()->OnAccountsInCookieUpdated(
accounts_in_cookie_jar_info, GoogleServiceAuthError::AuthErrorNone());
ExpectRelationReport(ReportingType::ON_CHANGE, histogram_tester,
AccountRelation::SINGLE_SIGNED_IN_MATCH_NO_SIGNED_OUT);
// Simulate a sign out of the content area.
const HistogramTester histogram_tester2;
accounts_in_cookie_jar_info = {/*accounts_are_fresh=*/true, no_accounts,
just_one};
investigator()->OnAccountsInCookieUpdated(
accounts_in_cookie_jar_info, GoogleServiceAuthError::AuthErrorNone());
const AccountRelation expected_relation =
AccountRelation::NO_SIGNED_IN_SINGLE_SIGNED_OUT_MATCH;
ExpectSharedReportHistograms(ReportingType::ON_CHANGE, histogram_tester2,
nullptr, 0, 1, 1, &expected_relation, true);
}
TEST_F(AccountInvestigatorTest, Initialize) {
EXPECT_FALSE(*previously_authenticated());
EXPECT_FALSE(timer()->IsRunning());
investigator()->Initialize();
EXPECT_FALSE(*previously_authenticated());
EXPECT_TRUE(timer()->IsRunning());
investigator()->Shutdown();
EXPECT_FALSE(timer()->IsRunning());
}
TEST_F(AccountInvestigatorTest, InitializeSignedIn) {
identity_test_env()->SetPrimaryAccount("1@mail.com");
EXPECT_FALSE(*previously_authenticated());
investigator()->Initialize();
EXPECT_TRUE(*previously_authenticated());
}
TEST_F(AccountInvestigatorTest, TryPeriodicReportStale) {
investigator()->Initialize();
const HistogramTester histogram_tester;
TryPeriodicReport();
EXPECT_TRUE(*periodic_pending());
EXPECT_EQ(0u, histogram_tester.GetTotalCountsForPrefix("Signin.").size());
std::string email("f@bar.com");
identity_test_env()->SetCookieAccounts(
{{email, identity::GetTestGaiaIdForEmail(email)}});
EXPECT_FALSE(*periodic_pending());
ExpectSharedReportHistograms(ReportingType::PERIODIC, histogram_tester,
nullptr, 1, 0, 1, nullptr, false);
}
TEST_F(AccountInvestigatorTest, TryPeriodicReportEmpty) {
identity_test_env()->SetFreshnessOfAccountsInGaiaCookie(false);
const HistogramTester histogram_tester;
TryPeriodicReport();
EXPECT_FALSE(*periodic_pending());
ExpectSharedReportHistograms(ReportingType::PERIODIC, histogram_tester,
nullptr, 0, 0, 0, nullptr, false);
}
} // namespace