blob: 00d2d75dace2733f9e5ee9e5f74f215e71a5a9ed [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/metrics/demographics/demographic_metrics_provider.h"
#include <memory>
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "components/metrics/demographics/user_demographics.h"
#include "components/metrics/metrics_log_uploader.h"
#include "components/sync/base/sync_prefs.h"
#include "components/sync/test/test_sync_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
#include "third_party/metrics_proto/ukm/report.pb.h"
namespace metrics {
namespace {
constexpr int kTestBirthYear = 1983;
constexpr UserDemographicsProto::Gender kTestGender =
UserDemographicsProto::GENDER_FEMALE;
enum TestSyncServiceState {
NULL_SYNC_SERVICE,
SYNC_FEATURE_NOT_ENABLED,
SYNC_FEATURE_ENABLED,
SYNC_FEATURE_ENABLED_BUT_PAUSED,
// Represents the user clearing sync data via dashboard. On all platforms
// except ChromeOS (Ash), this clears the primary account (which is basically
// SYNC_FEATURE_NOT_ENABLED). On ChromeOS Ash, Sync enters a special state.
SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD,
};
// Profile client for testing that gets fake Profile information and services.
class TestProfileClient : public DemographicMetricsProvider::ProfileClient {
public:
TestProfileClient(const TestProfileClient&) = delete;
TestProfileClient& operator=(const TestProfileClient&) = delete;
~TestProfileClient() override = default;
TestProfileClient(int number_of_profiles,
TestSyncServiceState sync_service_state)
: number_of_profiles_(number_of_profiles) {
RegisterDemographicsLocalStatePrefs(pref_service_.registry());
RegisterDemographicsProfilePrefs(pref_service_.registry());
switch (sync_service_state) {
case NULL_SYNC_SERVICE:
break;
case SYNC_FEATURE_NOT_ENABLED:
sync_service_ = std::make_unique<syncer::TestSyncService>();
// Set an arbitrary disable reason to mimic sync feature being unable to
// start.
sync_service_->SetDisableReasons(
{syncer::SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR});
break;
case SYNC_FEATURE_ENABLED:
// TestSyncService by default behaves as everything enabled/active.
sync_service_ = std::make_unique<syncer::TestSyncService>();
CHECK(sync_service_->GetDisableReasons().Empty());
CHECK_EQ(syncer::SyncService::TransportState::ACTIVE,
sync_service_->GetTransportState());
break;
case SYNC_FEATURE_ENABLED_BUT_PAUSED:
sync_service_ = std::make_unique<syncer::TestSyncService>();
// Mimic the user signing out from content are (sync paused).
sync_service_->SetPersistentAuthError();
CHECK(sync_service_->GetDisableReasons().Empty());
CHECK_EQ(syncer::SyncService::TransportState::PAUSED,
sync_service_->GetTransportState());
break;
case SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD:
sync_service_ = std::make_unique<syncer::TestSyncService>();
sync_service_->SetDisableReasons(
{syncer::SyncService::DISABLE_REASON_USER_CHOICE});
// On ChromeOS Ash, IsFirstSetupComplete gets cleared temporarily but
// immediately afterwards, it gets set again with
// ENGINE_INITIALIZED_WITH_AUTO_START. And yet, IsSyncFeatureEnabled()
// stays false because the user needs to manually resume sync the
// feature.
CHECK(sync_service_->GetUserSettings()->IsFirstSetupComplete());
CHECK(!sync_service_->IsSyncFeatureEnabled());
break;
}
}
int GetNumberOfProfilesOnDisk() override { return number_of_profiles_; }
syncer::SyncService* GetSyncService() override { return sync_service_.get(); }
PrefService* GetLocalState() override { return &pref_service_; }
PrefService* GetProfilePrefs() override { return &pref_service_; }
base::Time GetNetworkTime() const override {
base::Time time;
auto result = base::Time::FromString("17 Jun 2019 00:00:00 UDT", &time);
DCHECK(result);
return time;
}
void SetDemographicsInPrefs(int birth_year,
metrics::UserDemographicsProto_Gender gender) {
base::Value::Dict dict;
dict.Set(kSyncDemographicsBirthYearPath, birth_year);
dict.Set(kSyncDemographicsGenderPath, static_cast<int>(gender));
pref_service_.SetDict(kSyncDemographicsPrefName, std::move(dict));
}
private:
sync_preferences::TestingPrefServiceSyncable pref_service_;
std::unique_ptr<syncer::TestSyncService> sync_service_;
const int number_of_profiles_;
base::SimpleTestClock clock_;
};
TEST(DemographicMetricsProviderTest,
ProvideSyncedUserNoisedBirthYearAndGender_FeatureEnabled) {
base::HistogramTester histogram;
auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
SYNC_FEATURE_ENABLED);
client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
// Set birth year noise offset to not have it randomized.
const int kBirthYearOffset = 3;
client->GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
kBirthYearOffset);
// Run demographics provider.
DemographicMetricsProvider provider(
std::move(client), MetricsLogUploader::MetricServiceType::UMA);
ChromeUserMetricsExtension uma_proto;
provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
// Verify provided demographics.
EXPECT_EQ(kTestBirthYear + kBirthYearOffset,
uma_proto.user_demographics().birth_year());
EXPECT_EQ(kTestGender, uma_proto.user_demographics().gender());
// Verify histograms.
histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
UserDemographicsStatus::kSuccess, 1);
}
TEST(DemographicMetricsProviderTest,
ProvideSyncedUserNoisedBirthYearAndGender_NoSyncService) {
base::HistogramTester histogram;
auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
NULL_SYNC_SERVICE);
// Run demographics provider.
DemographicMetricsProvider provider(
std::move(client), MetricsLogUploader::MetricServiceType::UMA);
ChromeUserMetricsExtension uma_proto;
provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
// Expect the proto fields to be not set and left to default.
EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
EXPECT_FALSE(uma_proto.user_demographics().has_gender());
// Verify histograms.
histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
UserDemographicsStatus::kNoSyncService, 1);
}
TEST(DemographicMetricsProviderTest,
ProvideSyncedUserNoisedBirthYearAndGender_SyncEnabledButPaused) {
base::HistogramTester histogram;
auto client = std::make_unique<TestProfileClient>(
/*number_of_profiles=*/1, SYNC_FEATURE_ENABLED_BUT_PAUSED);
// Run demographics provider.
DemographicMetricsProvider provider(
std::move(client), MetricsLogUploader::MetricServiceType::UMA);
ChromeUserMetricsExtension uma_proto;
provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
// Expect the proto fields to be not set and left to default.
EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
EXPECT_FALSE(uma_proto.user_demographics().has_gender());
// Verify histograms.
histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
UserDemographicsStatus::kSyncNotEnabled, 1);
}
TEST(
DemographicMetricsProviderTest,
ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledOnChromeOsAshViaSyncDashboard) {
base::HistogramTester histogram;
auto client = std::make_unique<TestProfileClient>(
/*number_of_profiles=*/1,
SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD);
// Run demographics provider.
DemographicMetricsProvider provider(
std::move(client), MetricsLogUploader::MetricServiceType::UMA);
ChromeUserMetricsExtension uma_proto;
provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
// Expect the proto fields to be not set and left to default.
EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
EXPECT_FALSE(uma_proto.user_demographics().has_gender());
// Verify histograms.
histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
UserDemographicsStatus::kSyncNotEnabled, 1);
}
TEST(DemographicMetricsProviderTest,
ProvideSyncedUserNoisedBirthYearAndGender_SyncNotEnabled) {
base::HistogramTester histogram;
auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
SYNC_FEATURE_NOT_ENABLED);
// Run demographics provider.
DemographicMetricsProvider provider(
std::move(client), MetricsLogUploader::MetricServiceType::UMA);
ChromeUserMetricsExtension uma_proto;
provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
// Expect the proto fields to be not set and left to default.
EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
EXPECT_FALSE(uma_proto.user_demographics().has_gender());
// Verify histograms.
histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
UserDemographicsStatus::kSyncNotEnabled, 1);
}
TEST(DemographicMetricsProviderTest,
ProvideSyncedUserNoisedBirthYearAndGender_FeatureDisabled) {
// Disable demographics reporting feature.
base::test::ScopedFeatureList local_feature;
local_feature.InitAndDisableFeature(kDemographicMetricsReporting);
base::HistogramTester histogram;
auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
SYNC_FEATURE_ENABLED);
client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
// Run demographics provider.
DemographicMetricsProvider provider(
std::move(client), MetricsLogUploader::MetricServiceType::UMA);
ChromeUserMetricsExtension uma_proto;
provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
// Expect that the UMA proto is untouched.
EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
EXPECT_FALSE(uma_proto.user_demographics().has_gender());
// Verify that there are no histograms for user demographics.
histogram.ExpectTotalCount("UMA.UserDemographics.Status", 0);
}
TEST(DemographicMetricsProviderTest,
ProvideSyncedUserNoisedBirthYearAndGender_NotExactlyOneProfile) {
base::HistogramTester histogram;
auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/2,
SYNC_FEATURE_ENABLED);
client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
// Run demographics provider with not exactly one Profile on disk.
DemographicMetricsProvider provider(
std::move(client), MetricsLogUploader::MetricServiceType::UMA);
ChromeUserMetricsExtension uma_proto;
provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
#if !BUILDFLAG(IS_CHROMEOS_ASH)
// Expect that the UMA proto is untouched.
EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
EXPECT_FALSE(uma_proto.user_demographics().has_gender());
// Verify histograms.
histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
UserDemographicsStatus::kMoreThanOneProfile, 1);
#else
// On ChromeOS, we have a profile selection strategy, so expect UMA reporting
// to work.
EXPECT_TRUE(uma_proto.user_demographics().has_birth_year());
EXPECT_TRUE(uma_proto.user_demographics().has_gender());
// Verify histograms.
histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
UserDemographicsStatus::kSuccess, 1);
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
}
TEST(DemographicMetricsProviderTest,
ProvideSyncedUserNoisedBirthYearAndGender_NoUserDemographics) {
base::HistogramTester histogram;
auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
SYNC_FEATURE_ENABLED);
// Set some ineligible values to prefs.
client->SetDemographicsInPrefs(/*birth_year=*/-17,
UserDemographicsProto::GENDER_UNKNOWN);
// Run demographics provider with a ProfileClient that does not provide
// demographics because of some error.
DemographicMetricsProvider provider(
std::move(client), MetricsLogUploader::MetricServiceType::UMA);
ChromeUserMetricsExtension uma_proto;
provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
// Expect that the UMA proto is untouched.
EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
EXPECT_FALSE(uma_proto.user_demographics().has_gender());
// Verify that there are no histograms for user demographics.
histogram.ExpectUniqueSample(
"UMA.UserDemographics.Status",
UserDemographicsStatus::kIneligibleDemographicsData, 1);
}
TEST(DemographicMetricsProviderTest,
ProvideSyncedUserNoisedBirthYearAndGenderToUkmReport) {
base::HistogramTester histogram;
auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
SYNC_FEATURE_ENABLED);
client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
// Set birth year noise offset to not have it randomized.
const int kBirthYearOffset = 3;
client->GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
kBirthYearOffset);
// Run demographics provider.
DemographicMetricsProvider provider(
std::move(client), MetricsLogUploader::MetricServiceType::UKM);
ukm::Report report;
provider.ProvideSyncedUserNoisedBirthYearAndGenderToReport(&report);
// Verify provided demographics.
EXPECT_EQ(kTestBirthYear + kBirthYearOffset,
report.user_demographics().birth_year());
EXPECT_EQ(kTestGender, report.user_demographics().gender());
// Verify histograms.
histogram.ExpectUniqueSample("UKM.UserDemographics.Status",
UserDemographicsStatus::kSuccess, 1);
}
} // namespace
} // namespace metrics