1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/metrics/demographics/demographic_metrics_provider.h"
6
7 #include <memory>
8
9 #include "base/test/metrics/histogram_tester.h"
10 #include "base/test/scoped_feature_list.h"
11 #include "base/test/simple_test_clock.h"
12 #include "base/time/time.h"
13 #include "base/values.h"
14 #include "build/chromeos_buildflags.h"
15 #include "components/metrics/demographics/user_demographics.h"
16 #include "components/metrics/metrics_log_uploader.h"
17 #include "components/sync/base/features.h"
18 #include "components/sync/service/sync_prefs.h"
19 #include "components/sync/test/test_sync_service.h"
20 #include "components/sync_preferences/testing_pref_service_syncable.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
24 #include "third_party/metrics_proto/ukm/report.pb.h"
25
26 namespace metrics {
27 namespace {
28
29 constexpr int kTestBirthYear = 1983;
30 constexpr UserDemographicsProto::Gender kTestGender =
31 UserDemographicsProto::GENDER_FEMALE;
32
33 enum TestSyncServiceState {
34 NULL_SYNC_SERVICE,
35 SYNC_FEATURE_NOT_ENABLED,
36 SYNC_FEATURE_ENABLED,
37 SYNC_FEATURE_ENABLED_BUT_PAUSED,
38 SYNC_FEATURE_DISABLED_BUT_PREFERENCES_ENABLED,
39 #if BUILDFLAG(IS_CHROMEOS_ASH)
40 // Represents the user clearing sync data via dashboard. On all platforms
41 // except ChromeOS (Ash), this clears the primary account (which is basically
42 // SYNC_FEATURE_NOT_ENABLED). On ChromeOS Ash, Sync enters a special state.
43 SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD,
44 #endif // BUILDFLAG(IS_CHROMEOS_ASH)
45 };
46
47 // Profile client for testing that gets fake Profile information and services.
48 class TestProfileClient : public DemographicMetricsProvider::ProfileClient {
49 public:
50 TestProfileClient(const TestProfileClient&) = delete;
51 TestProfileClient& operator=(const TestProfileClient&) = delete;
52
53 ~TestProfileClient() override = default;
54
TestProfileClient(int number_of_profiles,TestSyncServiceState sync_service_state)55 TestProfileClient(int number_of_profiles,
56 TestSyncServiceState sync_service_state)
57 : number_of_profiles_(number_of_profiles) {
58 RegisterDemographicsLocalStatePrefs(pref_service_.registry());
59 RegisterDemographicsProfilePrefs(pref_service_.registry());
60
61 switch (sync_service_state) {
62 case NULL_SYNC_SERVICE:
63 break;
64
65 case SYNC_FEATURE_NOT_ENABLED:
66 sync_service_ = std::make_unique<syncer::TestSyncService>();
67 // Set an arbitrary disable reason to mimic sync feature being unable to
68 // start.
69 sync_service_->SetHasUnrecoverableError(true);
70 break;
71
72 case SYNC_FEATURE_ENABLED:
73 // TestSyncService by default behaves as everything enabled/active.
74 sync_service_ = std::make_unique<syncer::TestSyncService>();
75
76 CHECK(sync_service_->GetDisableReasons().empty());
77 CHECK_EQ(syncer::SyncService::TransportState::ACTIVE,
78 sync_service_->GetTransportState());
79 break;
80
81 case SYNC_FEATURE_ENABLED_BUT_PAUSED:
82 sync_service_ = std::make_unique<syncer::TestSyncService>();
83 // Mimic the user signing out from content are (sync paused).
84 sync_service_->SetPersistentAuthError();
85
86 CHECK(sync_service_->GetDisableReasons().empty());
87 CHECK_EQ(syncer::SyncService::TransportState::PAUSED,
88 sync_service_->GetTransportState());
89 break;
90
91 case SYNC_FEATURE_DISABLED_BUT_PREFERENCES_ENABLED:
92 sync_service_ = std::make_unique<syncer::TestSyncService>();
93 sync_service_->SetSignedIn(signin::ConsentLevel::kSignin);
94 CHECK(sync_service_->GetUserSettings()->GetSelectedTypes().Has(
95 syncer::UserSelectableType::kPreferences));
96 CHECK(!sync_service_->IsSyncFeatureEnabled());
97 CHECK(sync_service_->GetDisableReasons().empty());
98 CHECK_EQ(syncer::SyncService::TransportState::ACTIVE,
99 sync_service_->GetTransportState());
100 break;
101
102 #if BUILDFLAG(IS_CHROMEOS_ASH)
103 case SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD:
104 sync_service_ = std::make_unique<syncer::TestSyncService>();
105 sync_service_->GetUserSettings()->SetSyncFeatureDisabledViaDashboard(
106 true);
107
108 // On ChromeOS Ash, IsInitialSyncFeatureSetupComplete always returns
109 // true but IsSyncFeatureEnabled() stays false because the user needs to
110 // manually resume sync the feature.
111 CHECK(sync_service_->GetUserSettings()
112 ->IsInitialSyncFeatureSetupComplete());
113 CHECK(!sync_service_->IsSyncFeatureEnabled());
114 break;
115 #endif // BUILDFLAG(IS_CHROMEOS_ASH)
116 }
117 }
118
GetNumberOfProfilesOnDisk()119 int GetNumberOfProfilesOnDisk() override { return number_of_profiles_; }
120
GetSyncService()121 syncer::SyncService* GetSyncService() override { return sync_service_.get(); }
122
GetLocalState()123 PrefService* GetLocalState() override { return &pref_service_; }
124
GetProfilePrefs()125 PrefService* GetProfilePrefs() override { return &pref_service_; }
126
GetNetworkTime() const127 base::Time GetNetworkTime() const override {
128 base::Time time;
129 auto result = base::Time::FromString("17 Jun 2019 00:00:00 UDT", &time);
130 DCHECK(result);
131 return time;
132 }
133
SetDemographicsInPrefs(int birth_year,metrics::UserDemographicsProto_Gender gender)134 void SetDemographicsInPrefs(int birth_year,
135 metrics::UserDemographicsProto_Gender gender) {
136 base::Value::Dict dict;
137 dict.Set(kSyncDemographicsBirthYearPath, birth_year);
138 dict.Set(kSyncDemographicsGenderPath, static_cast<int>(gender));
139 pref_service_.SetDict(kSyncDemographicsPrefName, std::move(dict));
140 }
141
142 private:
143 sync_preferences::TestingPrefServiceSyncable pref_service_;
144 std::unique_ptr<syncer::TestSyncService> sync_service_;
145 const int number_of_profiles_;
146 base::SimpleTestClock clock_;
147 };
148
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_FeatureEnabled)149 TEST(DemographicMetricsProviderTest,
150 ProvideSyncedUserNoisedBirthYearAndGender_FeatureEnabled) {
151 base::HistogramTester histogram;
152
153 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
154 SYNC_FEATURE_ENABLED);
155 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
156
157 // Set birth year noise offset to not have it randomized.
158 const int kBirthYearOffset = 3;
159 client->GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
160 kBirthYearOffset);
161
162 // Run demographics provider.
163 DemographicMetricsProvider provider(
164 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
165 ChromeUserMetricsExtension uma_proto;
166 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
167
168 // Verify provided demographics.
169 EXPECT_EQ(kTestBirthYear + kBirthYearOffset,
170 uma_proto.user_demographics().birth_year());
171 EXPECT_EQ(kTestGender, uma_proto.user_demographics().gender());
172
173 // Verify histograms.
174 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
175 UserDemographicsStatus::kSuccess, 1);
176 }
177
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_NoSyncService)178 TEST(DemographicMetricsProviderTest,
179 ProvideSyncedUserNoisedBirthYearAndGender_NoSyncService) {
180 base::HistogramTester histogram;
181
182 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
183 NULL_SYNC_SERVICE);
184
185 // Run demographics provider.
186 DemographicMetricsProvider provider(
187 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
188 ChromeUserMetricsExtension uma_proto;
189 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
190
191 // Expect the proto fields to be not set and left to default.
192 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
193 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
194
195 // Verify histograms.
196 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
197 UserDemographicsStatus::kNoSyncService, 1);
198 }
199
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_SyncEnabledButPaused)200 TEST(DemographicMetricsProviderTest,
201 ProvideSyncedUserNoisedBirthYearAndGender_SyncEnabledButPaused) {
202 base::HistogramTester histogram;
203
204 auto client = std::make_unique<TestProfileClient>(
205 /*number_of_profiles=*/1, SYNC_FEATURE_ENABLED_BUT_PAUSED);
206
207 // Run demographics provider.
208 DemographicMetricsProvider provider(
209 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
210 ChromeUserMetricsExtension uma_proto;
211 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
212
213 // Expect the proto fields to be not set and left to default.
214 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
215 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
216
217 // Verify histograms.
218 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
219 UserDemographicsStatus::kSyncNotEnabled, 1);
220 }
221
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledButPreferencesEnabled_WithSyncToSignin)222 TEST(
223 DemographicMetricsProviderTest,
224 ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledButPreferencesEnabled_WithSyncToSignin) {
225 base::test::ScopedFeatureList sync_to_signin_enabled;
226 sync_to_signin_enabled.InitAndEnableFeature(
227 syncer::kReplaceSyncPromosWithSignInPromos);
228
229 base::HistogramTester histogram;
230
231 auto client = std::make_unique<TestProfileClient>(
232 /*number_of_profiles=*/1, SYNC_FEATURE_DISABLED_BUT_PREFERENCES_ENABLED);
233 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
234
235 // Set birth year noise offset to not have it randomized.
236 const int kBirthYearOffset = 3;
237 client->GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
238 kBirthYearOffset);
239
240 // Run demographics provider.
241 DemographicMetricsProvider provider(
242 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
243 ChromeUserMetricsExtension uma_proto;
244 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
245
246 // Verify provided demographics.
247 EXPECT_EQ(kTestBirthYear + kBirthYearOffset,
248 uma_proto.user_demographics().birth_year());
249 EXPECT_EQ(kTestGender, uma_proto.user_demographics().gender());
250
251 // Verify histograms: Demographics should be provided.
252 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
253 UserDemographicsStatus::kSuccess, 1);
254 }
255
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledButPreferencesEnabled_WithoutSyncToSignin)256 TEST(
257 DemographicMetricsProviderTest,
258 ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledButPreferencesEnabled_WithoutSyncToSignin) {
259 base::test::ScopedFeatureList sync_to_signin_disabled;
260 sync_to_signin_disabled.InitAndDisableFeature(
261 syncer::kReplaceSyncPromosWithSignInPromos);
262
263 base::HistogramTester histogram;
264
265 auto client = std::make_unique<TestProfileClient>(
266 /*number_of_profiles=*/1, SYNC_FEATURE_DISABLED_BUT_PREFERENCES_ENABLED);
267 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
268
269 // Set birth year noise offset to not have it randomized.
270 const int kBirthYearOffset = 3;
271 client->GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
272 kBirthYearOffset);
273
274 // Run demographics provider.
275 DemographicMetricsProvider provider(
276 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
277 ChromeUserMetricsExtension uma_proto;
278 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
279
280 // Expect the proto fields to be not set and left to default.
281 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
282 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
283
284 // Verify histograms: Demographics should NOT be provided.
285 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
286 UserDemographicsStatus::kSyncNotEnabled, 1);
287 }
288
289 #if BUILDFLAG(IS_CHROMEOS_ASH)
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledOnChromeOsAshViaSyncDashboard)290 TEST(
291 DemographicMetricsProviderTest,
292 ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledOnChromeOsAshViaSyncDashboard) {
293 base::HistogramTester histogram;
294
295 auto client = std::make_unique<TestProfileClient>(
296 /*number_of_profiles=*/1,
297 SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD);
298
299 // Run demographics provider.
300 DemographicMetricsProvider provider(
301 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
302 ChromeUserMetricsExtension uma_proto;
303 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
304
305 // Expect the proto fields to be not set and left to default.
306 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
307 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
308
309 // Verify histograms.
310 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
311 UserDemographicsStatus::kSyncNotEnabled, 1);
312 }
313 #endif // BUILDFLAG(IS_CHROMEOS_ASH)
314
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_SyncNotEnabled)315 TEST(DemographicMetricsProviderTest,
316 ProvideSyncedUserNoisedBirthYearAndGender_SyncNotEnabled) {
317 base::HistogramTester histogram;
318
319 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
320 SYNC_FEATURE_NOT_ENABLED);
321
322 // Run demographics provider.
323 DemographicMetricsProvider provider(
324 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
325 ChromeUserMetricsExtension uma_proto;
326 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
327
328 // Expect the proto fields to be not set and left to default.
329 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
330 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
331
332 // Verify histograms.
333 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
334 UserDemographicsStatus::kSyncNotEnabled, 1);
335 }
336
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_FeatureDisabled)337 TEST(DemographicMetricsProviderTest,
338 ProvideSyncedUserNoisedBirthYearAndGender_FeatureDisabled) {
339 // Disable demographics reporting feature.
340 base::test::ScopedFeatureList local_feature;
341 local_feature.InitAndDisableFeature(kDemographicMetricsReporting);
342
343 base::HistogramTester histogram;
344
345 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
346 SYNC_FEATURE_ENABLED);
347 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
348
349 // Run demographics provider.
350 DemographicMetricsProvider provider(
351 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
352 ChromeUserMetricsExtension uma_proto;
353 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
354
355 // Expect that the UMA proto is untouched.
356 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
357 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
358
359 // Verify that there are no histograms for user demographics.
360 histogram.ExpectTotalCount("UMA.UserDemographics.Status", 0);
361 }
362
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_NotExactlyOneProfile)363 TEST(DemographicMetricsProviderTest,
364 ProvideSyncedUserNoisedBirthYearAndGender_NotExactlyOneProfile) {
365 base::HistogramTester histogram;
366
367 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/2,
368 SYNC_FEATURE_ENABLED);
369 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
370
371 // Run demographics provider with not exactly one Profile on disk.
372 DemographicMetricsProvider provider(
373 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
374 ChromeUserMetricsExtension uma_proto;
375 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
376
377 #if !BUILDFLAG(IS_CHROMEOS_ASH)
378 // Expect that the UMA proto is untouched.
379 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
380 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
381
382 // Verify histograms.
383 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
384 UserDemographicsStatus::kMoreThanOneProfile, 1);
385 #else
386 // On ChromeOS, we have a profile selection strategy, so expect UMA reporting
387 // to work.
388 EXPECT_TRUE(uma_proto.user_demographics().has_birth_year());
389 EXPECT_TRUE(uma_proto.user_demographics().has_gender());
390
391 // Verify histograms.
392 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
393 UserDemographicsStatus::kSuccess, 1);
394 #endif // !BUILDFLAG(IS_CHROMEOS_ASH)
395 }
396
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_NoUserDemographics)397 TEST(DemographicMetricsProviderTest,
398 ProvideSyncedUserNoisedBirthYearAndGender_NoUserDemographics) {
399 base::HistogramTester histogram;
400
401 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
402 SYNC_FEATURE_ENABLED);
403 // Set some ineligible values to prefs.
404 client->SetDemographicsInPrefs(/*birth_year=*/-17,
405 UserDemographicsProto::GENDER_UNKNOWN);
406
407 // Run demographics provider with a ProfileClient that does not provide
408 // demographics because of some error.
409 DemographicMetricsProvider provider(
410 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
411 ChromeUserMetricsExtension uma_proto;
412 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
413
414 // Expect that the UMA proto is untouched.
415 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
416 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
417
418 // Verify that there are no histograms for user demographics.
419 histogram.ExpectUniqueSample(
420 "UMA.UserDemographics.Status",
421 UserDemographicsStatus::kIneligibleDemographicsData, 1);
422 }
423
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGenderToUkmReport)424 TEST(DemographicMetricsProviderTest,
425 ProvideSyncedUserNoisedBirthYearAndGenderToUkmReport) {
426 base::HistogramTester histogram;
427
428 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
429 SYNC_FEATURE_ENABLED);
430 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
431
432 // Set birth year noise offset to not have it randomized.
433 const int kBirthYearOffset = 3;
434 client->GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
435 kBirthYearOffset);
436
437 // Run demographics provider.
438 DemographicMetricsProvider provider(
439 std::move(client), MetricsLogUploader::MetricServiceType::UKM);
440 ukm::Report report;
441 provider.ProvideSyncedUserNoisedBirthYearAndGenderToReport(&report);
442
443 // Verify provided demographics.
444 EXPECT_EQ(kTestBirthYear + kBirthYearOffset,
445 report.user_demographics().birth_year());
446 EXPECT_EQ(kTestGender, report.user_demographics().gender());
447 }
448
449 } // namespace
450 } // namespace metrics
451