xref: /aosp_15_r20/external/cronet/components/metrics/demographics/user_demographics_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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/user_demographics.h"
6 
7 #include <utility>
8 
9 #include "base/time/time.h"
10 #include "base/values.h"
11 #include "components/sync_preferences/testing_pref_service_syncable.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "third_party/metrics_proto/user_demographics.pb.h"
14 
15 namespace metrics {
16 
17 namespace {
18 
19 // Gets the now time used for testing demographics.
GetNowTime()20 base::Time GetNowTime() {
21   constexpr char kNowTimeInStringFormat[] = "22 Jul 2019 00:00:00 UDT";
22 
23   base::Time now;
24   bool result = base::Time::FromString(kNowTimeInStringFormat, &now);
25   DCHECK(result);
26   return now;
27 }
28 
29 }  // namespace
30 
TEST(UserDemographicsTest,UserDemographicsResult_ForValue)31 TEST(UserDemographicsTest, UserDemographicsResult_ForValue) {
32   int user_birth_year = 1982;
33   UserDemographicsProto_Gender user_gender = UserDemographicsProto::GENDER_MALE;
34 
35   UserDemographics user_demographics;
36   user_demographics.birth_year = user_birth_year;
37   user_demographics.gender = user_gender;
38   UserDemographicsResult user_demographics_result =
39       UserDemographicsResult::ForValue(std::move(user_demographics));
40 
41   EXPECT_TRUE(user_demographics_result.IsSuccess());
42   EXPECT_EQ(UserDemographicsStatus::kSuccess,
43             user_demographics_result.status());
44   EXPECT_EQ(user_birth_year, user_demographics_result.value().birth_year);
45   EXPECT_EQ(user_gender, user_demographics_result.value().gender);
46 }
47 
TEST(UserDemographicsTest,UserDemographicsResult_ForStatus)48 TEST(UserDemographicsTest, UserDemographicsResult_ForStatus) {
49   UserDemographicsStatus error_status =
50       UserDemographicsStatus::kIneligibleDemographicsData;
51   UserDemographicsResult user_demographics_result =
52       UserDemographicsResult::ForStatus(error_status);
53 
54   EXPECT_FALSE(user_demographics_result.IsSuccess());
55   EXPECT_EQ(error_status, user_demographics_result.status());
56 }
57 
58 class UserDemographicsPrefsTest : public testing::Test {
59  protected:
UserDemographicsPrefsTest()60   UserDemographicsPrefsTest() {
61     RegisterDemographicsLocalStatePrefs(pref_service_.registry());
62     RegisterDemographicsProfilePrefs(pref_service_.registry());
63   }
64 
SetDemographics(int birth_year,UserDemographicsProto::Gender gender)65   void SetDemographics(int birth_year, UserDemographicsProto::Gender gender) {
66     SetDemographicsImpl(kSyncDemographicsPrefName, birth_year, gender);
67   }
68 
69 #if BUILDFLAG(IS_CHROMEOS_ASH)
SetOsDemographics(int birth_year,UserDemographicsProto::Gender gender)70   void SetOsDemographics(int birth_year, UserDemographicsProto::Gender gender) {
71     SetDemographicsImpl(kSyncOsDemographicsPrefName, birth_year, gender);
72   }
73 #endif
74 
GetLocalState()75   PrefService* GetLocalState() { return &pref_service_; }
GetProfilePrefs()76   PrefService* GetProfilePrefs() { return &pref_service_; }
77 
78  private:
SetDemographicsImpl(const std::string & pref_name,int birth_year,UserDemographicsProto::Gender gender)79   void SetDemographicsImpl(const std::string& pref_name,
80                            int birth_year,
81                            UserDemographicsProto::Gender gender) {
82     base::Value::Dict dict;
83     dict.Set(kSyncDemographicsBirthYearPath, birth_year);
84     dict.Set(kSyncDemographicsGenderPath, static_cast<int>(gender));
85     GetProfilePrefs()->SetDict(pref_name, std::move(dict));
86   }
87 
88   sync_preferences::TestingPrefServiceSyncable pref_service_;
89 };
90 
TEST_F(UserDemographicsPrefsTest,ReadDemographicsWithRandomOffset)91 TEST_F(UserDemographicsPrefsTest, ReadDemographicsWithRandomOffset) {
92   int user_demographics_birth_year = 1983;
93   UserDemographicsProto_Gender user_demographics_gender =
94       UserDemographicsProto::GENDER_MALE;
95 
96   // Set user demographic prefs.
97   SetDemographics(user_demographics_birth_year, user_demographics_gender);
98 
99   int provided_birth_year;
100   {
101     UserDemographicsResult demographics_result =
102         GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), GetLocalState(),
103                                                  GetProfilePrefs());
104     ASSERT_TRUE(demographics_result.IsSuccess());
105     EXPECT_EQ(user_demographics_gender, demographics_result.value().gender);
106     // Verify that the provided birth year is within the range.
107     provided_birth_year = demographics_result.value().birth_year;
108     int delta = provided_birth_year - user_demographics_birth_year;
109     EXPECT_LE(delta, kUserDemographicsBirthYearNoiseOffsetRange);
110     EXPECT_GE(delta, -kUserDemographicsBirthYearNoiseOffsetRange);
111   }
112 
113   // Verify that the offset is cached and that the randomized birth year is the
114   // same when doing more that one read of the birth year.
115   {
116     ASSERT_TRUE(
117         GetLocalState()->HasPrefPath(kUserDemographicsBirthYearOffsetPrefName));
118     UserDemographicsResult demographics_result =
119         GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), GetLocalState(),
120                                                  GetProfilePrefs());
121     ASSERT_TRUE(demographics_result.IsSuccess());
122     EXPECT_EQ(provided_birth_year, demographics_result.value().birth_year);
123   }
124 }
125 
126 #if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(UserDemographicsPrefsTest,ReadOsDemographicsWithRandomOffset)127 TEST_F(UserDemographicsPrefsTest, ReadOsDemographicsWithRandomOffset) {
128   int user_demographics_birth_year = 1983;
129   UserDemographicsProto_Gender user_demographics_gender =
130       UserDemographicsProto::GENDER_MALE;
131 
132   // Set user demographic prefs.
133   SetOsDemographics(user_demographics_birth_year, user_demographics_gender);
134 
135   int provided_birth_year;
136   {
137     UserDemographicsResult demographics_result =
138         GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), GetLocalState(),
139                                                  GetProfilePrefs());
140     ASSERT_TRUE(demographics_result.IsSuccess());
141     EXPECT_EQ(user_demographics_gender, demographics_result.value().gender);
142     // Verify that the provided birth year is within the range.
143     provided_birth_year = demographics_result.value().birth_year;
144     int delta = provided_birth_year - user_demographics_birth_year;
145     EXPECT_LE(delta, kUserDemographicsBirthYearNoiseOffsetRange);
146     EXPECT_GE(delta, -kUserDemographicsBirthYearNoiseOffsetRange);
147   }
148 
149   // Verify that the offset is cached and that the randomized birth year is the
150   // same when doing more that one read of the birth year.
151   {
152     ASSERT_TRUE(
153         GetLocalState()->HasPrefPath(kUserDemographicsBirthYearOffsetPrefName));
154     UserDemographicsResult demographics_result =
155         GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), GetLocalState(),
156                                                  GetProfilePrefs());
157     ASSERT_TRUE(demographics_result.IsSuccess());
158     EXPECT_EQ(provided_birth_year, demographics_result.value().birth_year);
159   }
160 }
161 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
162 
TEST_F(UserDemographicsPrefsTest,ReadAndClearUserDemographicPreferences)163 TEST_F(UserDemographicsPrefsTest, ReadAndClearUserDemographicPreferences) {
164   // Verify demographic prefs are not available when there is nothing set.
165   ASSERT_FALSE(GetUserNoisedBirthYearAndGenderFromPrefs(
166                    GetNowTime(), GetLocalState(), GetProfilePrefs())
167                    .IsSuccess());
168 
169   // Set demographic prefs directly from the pref service interface because
170   // demographic prefs will only be set on the server-side. The SyncPrefs
171   // interface cannot set demographic prefs.
172   SetDemographics(1983, UserDemographicsProto::GENDER_FEMALE);
173 
174   // Set birth year noise offset to not have it randomized.
175   GetProfilePrefs()->SetInteger(kUserDemographicsBirthYearOffsetPrefName, 2);
176 
177   // Verify that demographics are provided.
178   {
179     UserDemographicsResult demographics_result =
180         GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), GetLocalState(),
181                                                  GetProfilePrefs());
182     ASSERT_TRUE(demographics_result.IsSuccess());
183   }
184 
185   ClearDemographicsPrefs(GetProfilePrefs());
186 
187   // Verify that demographics are not provided and kSyncDemographics is cleared.
188   // Note that we retain k*DemographicsBirthYearOffset. If the user resumes
189   // syncing, causing these prefs to be recreated, we don't want them to start
190   // reporting a different randomized birth year as this could narrow down or
191   // even reveal their true birth year.
192   EXPECT_FALSE(GetUserNoisedBirthYearAndGenderFromPrefs(
193                    GetNowTime(), GetLocalState(), GetProfilePrefs())
194                    .IsSuccess());
195   EXPECT_FALSE(GetProfilePrefs()->HasPrefPath(kSyncDemographicsPrefName));
196 #if BUILDFLAG(IS_CHROMEOS_ASH)
197   EXPECT_FALSE(GetProfilePrefs()->HasPrefPath(kSyncOsDemographicsPrefName));
198 #endif
199   EXPECT_TRUE(
200       GetLocalState()->HasPrefPath(kUserDemographicsBirthYearOffsetPrefName));
201   // Deprecated offset is not created if it does not already exist.
202   EXPECT_FALSE(GetProfilePrefs()->HasPrefPath(
203       kDeprecatedDemographicsBirthYearOffsetPrefName));
204 }
205 
TEST_F(UserDemographicsPrefsTest,ReadAndClearDeprecatedOffsetPref)206 TEST_F(UserDemographicsPrefsTest, ReadAndClearDeprecatedOffsetPref) {
207   // Verify demographic prefs are not available when there is nothing set.
208   ASSERT_FALSE(GetUserNoisedBirthYearAndGenderFromPrefs(
209                    GetNowTime(), GetLocalState(), GetProfilePrefs())
210                    .IsSuccess());
211 
212   // Set demographic prefs directly from the pref service interface because
213   // demographic prefs will only be set on the server-side. The SyncPrefs
214   // interface cannot set demographic prefs.
215   SetDemographics(1983, UserDemographicsProto::GENDER_FEMALE);
216 
217   // Set deprecated birth year noise offset in the UserPrefs
218   GetProfilePrefs()->SetInteger(kDeprecatedDemographicsBirthYearOffsetPrefName,
219                                 2);
220 
221   // Verify that demographics are provided.
222   {
223     UserDemographicsResult demographics_result =
224         GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), GetLocalState(),
225                                                  GetProfilePrefs());
226     ASSERT_TRUE(demographics_result.IsSuccess());
227   }
228 
229   // Offset if migrated to new pref.
230   EXPECT_EQ(
231       2, GetLocalState()->GetInteger(kUserDemographicsBirthYearOffsetPrefName));
232   // TODO(crbug/1367338): clear/remove deprecated pref after 2023/09
233   EXPECT_TRUE(GetProfilePrefs()->HasPrefPath(
234       kDeprecatedDemographicsBirthYearOffsetPrefName));
235 }
236 
237 #if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(UserDemographicsPrefsTest,ChromeOsAsh)238 TEST_F(UserDemographicsPrefsTest, ChromeOsAsh) {
239   // Verify demographic prefs are not available when there is nothing set.
240   ASSERT_FALSE(GetUserNoisedBirthYearAndGenderFromPrefs(
241                    GetNowTime(), GetLocalState(), GetProfilePrefs())
242                    .IsSuccess());
243 
244   // Set OS demographic prefs directly within the pref service interface.
245   SetOsDemographics(1983, UserDemographicsProto::GENDER_FEMALE);
246 
247   // Set  birth year noise offset in the UserPrefs
248   GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName, 2);
249 
250   // Verify that demographics are provided.
251   {
252     UserDemographicsResult demographics_result =
253         GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), GetLocalState(),
254                                                  GetProfilePrefs());
255     ASSERT_TRUE(demographics_result.IsSuccess());
256   }
257 
258   EXPECT_FALSE(GetProfilePrefs()->HasPrefPath(kSyncDemographicsPrefName));
259   EXPECT_TRUE(GetProfilePrefs()->HasPrefPath(kSyncOsDemographicsPrefName));
260   EXPECT_TRUE(
261       GetLocalState()->HasPrefPath(kUserDemographicsBirthYearOffsetPrefName));
262 }
263 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
264 
265 struct DemographicsTestParam {
266   // Birth year of the user.
267   int birth_year = kUserDemographicsBirthYearDefaultValue;
268 
269   // Non-random offset to apply to |birth_year| as noise.
270   int birth_year_offset = kUserDemographicsBirthYearNoiseOffsetDefaultValue;
271 
272   // Gender of the user.
273   UserDemographicsProto_Gender gender = kUserDemographicGenderDefaultEnumValue;
274 
275   // Status of the retrieval of demographics.
276   UserDemographicsStatus status = UserDemographicsStatus::kMaxValue;
277 };
278 
279 // Extend UserDemographicsPrefsTest fixture for parameterized tests on
280 // demographics.
281 class UserDemographicsPrefsTestWithParam
282     : public UserDemographicsPrefsTest,
283       public testing::WithParamInterface<DemographicsTestParam> {};
284 
TEST_P(UserDemographicsPrefsTestWithParam,ReadDemographics_OffsetIsNotRandom)285 TEST_P(UserDemographicsPrefsTestWithParam, ReadDemographics_OffsetIsNotRandom) {
286   DemographicsTestParam param = GetParam();
287 
288   // Set user demographic prefs.
289   SetDemographics(param.birth_year, param.gender);
290 
291   // Set birth year noise offset to not have it randomized.
292   GetLocalState()->SetInteger(kUserDemographicsBirthYearOffsetPrefName,
293                               param.birth_year_offset);
294 
295   // Verify provided demographics for the different parameterized test cases.
296   UserDemographicsResult demographics_result =
297       GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), GetLocalState(),
298                                                GetProfilePrefs());
299   if (param.status == UserDemographicsStatus::kSuccess) {
300     ASSERT_TRUE(demographics_result.IsSuccess());
301     EXPECT_EQ(param.birth_year + param.birth_year_offset,
302               demographics_result.value().birth_year);
303     EXPECT_EQ(param.gender, demographics_result.value().gender);
304   } else {
305     ASSERT_FALSE(demographics_result.IsSuccess());
306     EXPECT_EQ(param.status, demographics_result.status());
307   }
308 }
309 
310 // Test suite composed of different test cases of getting user demographics.
311 // The now time in each test case is "22 Jul 2019 00:00:00 UDT" which falls into
312 // the year bucket of 2018. Users need at most a |birth_year| +
313 // |birth_year_offset| of 1998 to be able to provide demographics.
314 INSTANTIATE_TEST_SUITE_P(
315     All,
316     UserDemographicsPrefsTestWithParam,
317     ::testing::Values(
318         // Test where birth year should not be provided because |birth_year| + 2
319         // > 1998.
320         DemographicsTestParam{
321             /*birth_year=*/1997,
322             /*birth_year_offset=*/2,
323             /*gender=*/UserDemographicsProto::GENDER_FEMALE,
324             /*status=*/UserDemographicsStatus::kIneligibleDemographicsData},
325         // Test where birth year should not be provided because |birth_year| - 2
326         // > 1998.
327         DemographicsTestParam{
328             /*birth_year=*/2001,
329             /*birth_year_offset=*/-2,
330             /*gender=*/UserDemographicsProto::GENDER_FEMALE,
331             /*status=*/UserDemographicsStatus::kIneligibleDemographicsData},
332         // Test where birth year should not be provided because age of user is
333         // |kUserDemographicsMaxAge| + 1, which is over the max age.
334         DemographicsTestParam{
335             /*birth_year=*/1933,
336             /*birth_year_offset=*/0,
337             /*gender=*/UserDemographicsProto::GENDER_FEMALE,
338             /*status=*/UserDemographicsStatus::kIneligibleDemographicsData},
339         // Test where gender should not be provided because it has a low
340         // population that can have their privacy compromised because of high
341         // entropy.
342         DemographicsTestParam{
343             /*birth_year=*/1986,
344             /*birth_year_offset=*/0,
345             /*gender=*/UserDemographicsProto::GENDER_CUSTOM_OR_OTHER,
346             /*status=*/UserDemographicsStatus::kIneligibleDemographicsData},
347         // Test where birth year can be provided because |birth_year| + 2 ==
348         // 1998.
349         DemographicsTestParam{/*birth_year=*/1996,
350                               /*birth_year_offset=*/2,
351                               /*gender=*/UserDemographicsProto::GENDER_FEMALE,
352                               /*status=*/UserDemographicsStatus::kSuccess},
353         // Test where birth year can be provided because |birth_year| - 2 ==
354         // 1998.
355         DemographicsTestParam{/*birth_year=*/2000,
356                               /*birth_year_offset=*/-2,
357                               /*gender=*/UserDemographicsProto::GENDER_MALE,
358                               /*status=*/UserDemographicsStatus::kSuccess},
359         // Test where birth year can be provided because |birth_year| + 2 <
360         // 1998.
361         DemographicsTestParam{/*birth_year=*/1995,
362                               /*birth_year_offset=*/2,
363                               /*gender=*/UserDemographicsProto::GENDER_FEMALE,
364                               /*status=*/UserDemographicsStatus::kSuccess},
365         // Test where birth year can be provided because |birth_year| - 2 <
366         // 1998.
367         DemographicsTestParam{/*birth_year=*/1999,
368                               /*birth_year_offset=*/-2,
369                               /*gender=*/UserDemographicsProto::GENDER_MALE,
370                               /*status=*/UserDemographicsStatus::kSuccess},
371         // Test where gender can be provided because it is part of a large
372         // population with a low entropy.
373         DemographicsTestParam{/*birth_year=*/1986,
374                               /*birth_year_offset=*/0,
375                               /*gender=*/UserDemographicsProto::GENDER_FEMALE,
376                               /*status=*/UserDemographicsStatus::kSuccess},
377         // Test where gender can be provided because it is part of a large
378         // population with a low entropy.
379         DemographicsTestParam{/*birth_year=*/1986,
380                               /*birth_year_offset=*/0,
381                               /*gender=*/UserDemographicsProto::GENDER_MALE,
382                               /*status=*/UserDemographicsStatus::kSuccess}));
383 
384 }  // namespace metrics
385