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