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_->SetDisableReasons(
70 {syncer::SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR});
71 break;
72
73 case SYNC_FEATURE_ENABLED:
74 // TestSyncService by default behaves as everything enabled/active.
75 sync_service_ = std::make_unique<syncer::TestSyncService>();
76
77 CHECK(sync_service_->GetDisableReasons().empty());
78 CHECK_EQ(syncer::SyncService::TransportState::ACTIVE,
79 sync_service_->GetTransportState());
80 break;
81
82 case SYNC_FEATURE_ENABLED_BUT_PAUSED:
83 sync_service_ = std::make_unique<syncer::TestSyncService>();
84 // Mimic the user signing out from content are (sync paused).
85 sync_service_->SetPersistentAuthError();
86
87 CHECK(sync_service_->GetDisableReasons().empty());
88 CHECK_EQ(syncer::SyncService::TransportState::PAUSED,
89 sync_service_->GetTransportState());
90 break;
91
92 case SYNC_FEATURE_DISABLED_BUT_PREFERENCES_ENABLED:
93 sync_service_ = std::make_unique<syncer::TestSyncService>();
94 sync_service_->SetHasSyncConsent(false);
95 CHECK(sync_service_->GetUserSettings()->GetSelectedTypes().Has(
96 syncer::UserSelectableType::kPreferences));
97 CHECK(!sync_service_->IsSyncFeatureEnabled());
98 CHECK(sync_service_->GetDisableReasons().empty());
99 CHECK_EQ(syncer::SyncService::TransportState::ACTIVE,
100 sync_service_->GetTransportState());
101 break;
102
103 #if BUILDFLAG(IS_CHROMEOS_ASH)
104 case SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD:
105 sync_service_ = std::make_unique<syncer::TestSyncService>();
106 sync_service_->GetUserSettings()->SetSyncFeatureDisabledViaDashboard(
107 true);
108
109 // On ChromeOS Ash, IsInitialSyncFeatureSetupComplete always returns
110 // true but IsSyncFeatureEnabled() stays false because the user needs to
111 // manually resume sync the feature.
112 CHECK(sync_service_->GetUserSettings()
113 ->IsInitialSyncFeatureSetupComplete());
114 CHECK(!sync_service_->IsSyncFeatureEnabled());
115 break;
116 #endif // BUILDFLAG(IS_CHROMEOS_ASH)
117 }
118 }
119
GetNumberOfProfilesOnDisk()120 int GetNumberOfProfilesOnDisk() override { return number_of_profiles_; }
121
GetSyncService()122 syncer::SyncService* GetSyncService() override { return sync_service_.get(); }
123
GetLocalState()124 PrefService* GetLocalState() override { return &pref_service_; }
125
GetProfilePrefs()126 PrefService* GetProfilePrefs() override { return &pref_service_; }
127
GetNetworkTime() const128 base::Time GetNetworkTime() const override {
129 base::Time time;
130 auto result = base::Time::FromString("17 Jun 2019 00:00:00 UDT", &time);
131 DCHECK(result);
132 return time;
133 }
134
SetDemographicsInPrefs(int birth_year,metrics::UserDemographicsProto_Gender gender)135 void SetDemographicsInPrefs(int birth_year,
136 metrics::UserDemographicsProto_Gender gender) {
137 base::Value::Dict dict;
138 dict.Set(kSyncDemographicsBirthYearPath, birth_year);
139 dict.Set(kSyncDemographicsGenderPath, static_cast<int>(gender));
140 pref_service_.SetDict(kSyncDemographicsPrefName, std::move(dict));
141 }
142
143 private:
144 sync_preferences::TestingPrefServiceSyncable pref_service_;
145 std::unique_ptr<syncer::TestSyncService> sync_service_;
146 const int number_of_profiles_;
147 base::SimpleTestClock clock_;
148 };
149
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_FeatureEnabled)150 TEST(DemographicMetricsProviderTest,
151 ProvideSyncedUserNoisedBirthYearAndGender_FeatureEnabled) {
152 base::HistogramTester histogram;
153
154 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
155 SYNC_FEATURE_ENABLED);
156 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
157
158 // Set birth year noise offset to not have it randomized.
159 const int kBirthYearOffset = 3;
160 client->GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
161 kBirthYearOffset);
162
163 // Run demographics provider.
164 DemographicMetricsProvider provider(
165 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
166 ChromeUserMetricsExtension uma_proto;
167 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
168
169 // Verify provided demographics.
170 EXPECT_EQ(kTestBirthYear + kBirthYearOffset,
171 uma_proto.user_demographics().birth_year());
172 EXPECT_EQ(kTestGender, uma_proto.user_demographics().gender());
173
174 // Verify histograms.
175 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
176 UserDemographicsStatus::kSuccess, 1);
177 }
178
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_NoSyncService)179 TEST(DemographicMetricsProviderTest,
180 ProvideSyncedUserNoisedBirthYearAndGender_NoSyncService) {
181 base::HistogramTester histogram;
182
183 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
184 NULL_SYNC_SERVICE);
185
186 // Run demographics provider.
187 DemographicMetricsProvider provider(
188 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
189 ChromeUserMetricsExtension uma_proto;
190 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
191
192 // Expect the proto fields to be not set and left to default.
193 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
194 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
195
196 // Verify histograms.
197 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
198 UserDemographicsStatus::kNoSyncService, 1);
199 }
200
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_SyncEnabledButPaused)201 TEST(DemographicMetricsProviderTest,
202 ProvideSyncedUserNoisedBirthYearAndGender_SyncEnabledButPaused) {
203 base::HistogramTester histogram;
204
205 auto client = std::make_unique<TestProfileClient>(
206 /*number_of_profiles=*/1, SYNC_FEATURE_ENABLED_BUT_PAUSED);
207
208 // Run demographics provider.
209 DemographicMetricsProvider provider(
210 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
211 ChromeUserMetricsExtension uma_proto;
212 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
213
214 // Expect the proto fields to be not set and left to default.
215 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
216 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
217
218 // Verify histograms.
219 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
220 UserDemographicsStatus::kSyncNotEnabled, 1);
221 }
222
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledButPreferencesEnabled_WithSyncToSignin)223 TEST(
224 DemographicMetricsProviderTest,
225 ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledButPreferencesEnabled_WithSyncToSignin) {
226 base::test::ScopedFeatureList sync_to_signin_enabled;
227 sync_to_signin_enabled.InitAndEnableFeature(
228 syncer::kReplaceSyncPromosWithSignInPromos);
229
230 base::HistogramTester histogram;
231
232 auto client = std::make_unique<TestProfileClient>(
233 /*number_of_profiles=*/1, SYNC_FEATURE_DISABLED_BUT_PREFERENCES_ENABLED);
234 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
235
236 // Set birth year noise offset to not have it randomized.
237 const int kBirthYearOffset = 3;
238 client->GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
239 kBirthYearOffset);
240
241 // Run demographics provider.
242 DemographicMetricsProvider provider(
243 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
244 ChromeUserMetricsExtension uma_proto;
245 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
246
247 // Verify provided demographics.
248 EXPECT_EQ(kTestBirthYear + kBirthYearOffset,
249 uma_proto.user_demographics().birth_year());
250 EXPECT_EQ(kTestGender, uma_proto.user_demographics().gender());
251
252 // Verify histograms: Demographics should be provided.
253 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
254 UserDemographicsStatus::kSuccess, 1);
255 }
256
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledButPreferencesEnabled_WithoutSyncToSignin)257 TEST(
258 DemographicMetricsProviderTest,
259 ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledButPreferencesEnabled_WithoutSyncToSignin) {
260 base::test::ScopedFeatureList sync_to_signin_disabled;
261 sync_to_signin_disabled.InitAndDisableFeature(
262 syncer::kReplaceSyncPromosWithSignInPromos);
263
264 base::HistogramTester histogram;
265
266 auto client = std::make_unique<TestProfileClient>(
267 /*number_of_profiles=*/1, SYNC_FEATURE_DISABLED_BUT_PREFERENCES_ENABLED);
268 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
269
270 // Set birth year noise offset to not have it randomized.
271 const int kBirthYearOffset = 3;
272 client->GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
273 kBirthYearOffset);
274
275 // Run demographics provider.
276 DemographicMetricsProvider provider(
277 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
278 ChromeUserMetricsExtension uma_proto;
279 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
280
281 // Expect the proto fields to be not set and left to default.
282 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
283 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
284
285 // Verify histograms: Demographics should NOT be provided.
286 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
287 UserDemographicsStatus::kSyncNotEnabled, 1);
288 }
289
290 #if BUILDFLAG(IS_CHROMEOS_ASH)
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledOnChromeOsAshViaSyncDashboard)291 TEST(
292 DemographicMetricsProviderTest,
293 ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledOnChromeOsAshViaSyncDashboard) {
294 base::HistogramTester histogram;
295
296 auto client = std::make_unique<TestProfileClient>(
297 /*number_of_profiles=*/1,
298 SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD);
299
300 // Run demographics provider.
301 DemographicMetricsProvider provider(
302 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
303 ChromeUserMetricsExtension uma_proto;
304 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
305
306 // Expect the proto fields to be not set and left to default.
307 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
308 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
309
310 // Verify histograms.
311 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
312 UserDemographicsStatus::kSyncNotEnabled, 1);
313 }
314 #endif // BUILDFLAG(IS_CHROMEOS_ASH)
315
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_SyncNotEnabled)316 TEST(DemographicMetricsProviderTest,
317 ProvideSyncedUserNoisedBirthYearAndGender_SyncNotEnabled) {
318 base::HistogramTester histogram;
319
320 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
321 SYNC_FEATURE_NOT_ENABLED);
322
323 // Run demographics provider.
324 DemographicMetricsProvider provider(
325 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
326 ChromeUserMetricsExtension uma_proto;
327 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
328
329 // Expect the proto fields to be not set and left to default.
330 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
331 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
332
333 // Verify histograms.
334 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
335 UserDemographicsStatus::kSyncNotEnabled, 1);
336 }
337
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_FeatureDisabled)338 TEST(DemographicMetricsProviderTest,
339 ProvideSyncedUserNoisedBirthYearAndGender_FeatureDisabled) {
340 // Disable demographics reporting feature.
341 base::test::ScopedFeatureList local_feature;
342 local_feature.InitAndDisableFeature(kDemographicMetricsReporting);
343
344 base::HistogramTester histogram;
345
346 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
347 SYNC_FEATURE_ENABLED);
348 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
349
350 // Run demographics provider.
351 DemographicMetricsProvider provider(
352 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
353 ChromeUserMetricsExtension uma_proto;
354 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
355
356 // Expect that the UMA proto is untouched.
357 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
358 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
359
360 // Verify that there are no histograms for user demographics.
361 histogram.ExpectTotalCount("UMA.UserDemographics.Status", 0);
362 }
363
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_NotExactlyOneProfile)364 TEST(DemographicMetricsProviderTest,
365 ProvideSyncedUserNoisedBirthYearAndGender_NotExactlyOneProfile) {
366 base::HistogramTester histogram;
367
368 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/2,
369 SYNC_FEATURE_ENABLED);
370 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
371
372 // Run demographics provider with not exactly one Profile on disk.
373 DemographicMetricsProvider provider(
374 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
375 ChromeUserMetricsExtension uma_proto;
376 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
377
378 #if !BUILDFLAG(IS_CHROMEOS_ASH)
379 // Expect that the UMA proto is untouched.
380 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
381 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
382
383 // Verify histograms.
384 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
385 UserDemographicsStatus::kMoreThanOneProfile, 1);
386 #else
387 // On ChromeOS, we have a profile selection strategy, so expect UMA reporting
388 // to work.
389 EXPECT_TRUE(uma_proto.user_demographics().has_birth_year());
390 EXPECT_TRUE(uma_proto.user_demographics().has_gender());
391
392 // Verify histograms.
393 histogram.ExpectUniqueSample("UMA.UserDemographics.Status",
394 UserDemographicsStatus::kSuccess, 1);
395 #endif // !BUILDFLAG(IS_CHROMEOS_ASH)
396 }
397
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGender_NoUserDemographics)398 TEST(DemographicMetricsProviderTest,
399 ProvideSyncedUserNoisedBirthYearAndGender_NoUserDemographics) {
400 base::HistogramTester histogram;
401
402 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
403 SYNC_FEATURE_ENABLED);
404 // Set some ineligible values to prefs.
405 client->SetDemographicsInPrefs(/*birth_year=*/-17,
406 UserDemographicsProto::GENDER_UNKNOWN);
407
408 // Run demographics provider with a ProfileClient that does not provide
409 // demographics because of some error.
410 DemographicMetricsProvider provider(
411 std::move(client), MetricsLogUploader::MetricServiceType::UMA);
412 ChromeUserMetricsExtension uma_proto;
413 provider.ProvideSyncedUserNoisedBirthYearAndGender(&uma_proto);
414
415 // Expect that the UMA proto is untouched.
416 EXPECT_FALSE(uma_proto.user_demographics().has_birth_year());
417 EXPECT_FALSE(uma_proto.user_demographics().has_gender());
418
419 // Verify that there are no histograms for user demographics.
420 histogram.ExpectUniqueSample(
421 "UMA.UserDemographics.Status",
422 UserDemographicsStatus::kIneligibleDemographicsData, 1);
423 }
424
TEST(DemographicMetricsProviderTest,ProvideSyncedUserNoisedBirthYearAndGenderToUkmReport)425 TEST(DemographicMetricsProviderTest,
426 ProvideSyncedUserNoisedBirthYearAndGenderToUkmReport) {
427 base::HistogramTester histogram;
428
429 auto client = std::make_unique<TestProfileClient>(/*number_of_profiles=*/1,
430 SYNC_FEATURE_ENABLED);
431 client->SetDemographicsInPrefs(kTestBirthYear, kTestGender);
432
433 // Set birth year noise offset to not have it randomized.
434 const int kBirthYearOffset = 3;
435 client->GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
436 kBirthYearOffset);
437
438 // Run demographics provider.
439 DemographicMetricsProvider provider(
440 std::move(client), MetricsLogUploader::MetricServiceType::UKM);
441 ukm::Report report;
442 provider.ProvideSyncedUserNoisedBirthYearAndGenderToReport(&report);
443
444 // Verify provided demographics.
445 EXPECT_EQ(kTestBirthYear + kBirthYearOffset,
446 report.user_demographics().birth_year());
447 EXPECT_EQ(kTestGender, report.user_demographics().gender());
448 }
449
450 } // namespace
451 } // namespace metrics
452