372 lines
14 KiB
C++
372 lines
14 KiB
C++
// 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::DisableReasonSet(
|
|
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
|