1 // Copyright 2019 The Chromium Authors. All rights reserved.
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 "components/sync_preferences/testing_pref_service_syncable.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/metrics_proto/user_demographics.pb.h"
12 
13 namespace metrics {
14 
15 namespace {
16 
17 // Gets the now time used for testing demographics.
GetNowTime()18 base::Time GetNowTime() {
19   constexpr char kNowTimeInStringFormat[] = "22 Jul 2019 00:00:00 UDT";
20 
21   base::Time now;
22   bool result = base::Time::FromString(kNowTimeInStringFormat, &now);
23   DCHECK(result);
24   return now;
25 }
26 
27 }  // namespace
28 
TEST(UserDemographicsTest,UserDemographicsResult_ForValue)29 TEST(UserDemographicsTest, UserDemographicsResult_ForValue) {
30   int user_birth_year = 1982;
31   UserDemographicsProto_Gender user_gender = UserDemographicsProto::GENDER_MALE;
32 
33   UserDemographics user_demographics;
34   user_demographics.birth_year = user_birth_year;
35   user_demographics.gender = user_gender;
36   UserDemographicsResult user_demographics_result =
37       UserDemographicsResult::ForValue(std::move(user_demographics));
38 
39   EXPECT_TRUE(user_demographics_result.IsSuccess());
40   EXPECT_EQ(UserDemographicsStatus::kSuccess,
41             user_demographics_result.status());
42   EXPECT_EQ(user_birth_year, user_demographics_result.value().birth_year);
43   EXPECT_EQ(user_gender, user_demographics_result.value().gender);
44 }
45 
TEST(UserDemographicsTest,UserDemographicsResult_ForStatus)46 TEST(UserDemographicsTest, UserDemographicsResult_ForStatus) {
47   UserDemographicsStatus error_status =
48       UserDemographicsStatus::kIneligibleDemographicsData;
49   UserDemographicsResult user_demographics_result =
50       UserDemographicsResult::ForStatus(error_status);
51 
52   EXPECT_FALSE(user_demographics_result.IsSuccess());
53   EXPECT_EQ(error_status, user_demographics_result.status());
54 }
55 
56 class UserDemographicsPrefsTest : public testing::Test {
57  protected:
UserDemographicsPrefsTest()58   UserDemographicsPrefsTest() {
59     RegisterDemographicsProfilePrefs(pref_service_.registry());
60   }
61 
SetDemographics(int birth_year,UserDemographicsProto::Gender gender)62   void SetDemographics(int birth_year, UserDemographicsProto::Gender gender) {
63     base::DictionaryValue dict;
64     dict.SetIntPath(kSyncDemographicsBirthYearPath, birth_year);
65     dict.SetIntPath(kSyncDemographicsGenderPath, static_cast<int>(gender));
66     pref_service_.Set(kSyncDemographicsPrefName, dict);
67   }
68 
69   sync_preferences::TestingPrefServiceSyncable pref_service_;
70 };
71 
TEST_F(UserDemographicsPrefsTest,ReadDemographicsWithRandomOffset)72 TEST_F(UserDemographicsPrefsTest, ReadDemographicsWithRandomOffset) {
73   int user_demographics_birth_year = 1983;
74   UserDemographicsProto_Gender user_demographics_gender =
75       UserDemographicsProto::GENDER_MALE;
76 
77   // Set user demographic prefs.
78   SetDemographics(user_demographics_birth_year, user_demographics_gender);
79 
80   int provided_birth_year;
81   {
82     UserDemographicsResult demographics_result =
83         GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), &pref_service_);
84     ASSERT_TRUE(demographics_result.IsSuccess());
85     EXPECT_EQ(user_demographics_gender, demographics_result.value().gender);
86     // Verify that the provided birth year is within the range.
87     provided_birth_year = demographics_result.value().birth_year;
88     int delta = provided_birth_year - user_demographics_birth_year;
89     EXPECT_LE(delta, kUserDemographicsBirthYearNoiseOffsetRange);
90     EXPECT_GE(delta, -kUserDemographicsBirthYearNoiseOffsetRange);
91   }
92 
93   // Verify that the offset is cached and that the randomized birth year is the
94   // same when doing more that one read of the birth year.
95   {
96     ASSERT_TRUE(
97         pref_service_.HasPrefPath(kSyncDemographicsBirthYearOffsetPrefName));
98     UserDemographicsResult demographics_result =
99         GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), &pref_service_);
100     ASSERT_TRUE(demographics_result.IsSuccess());
101     EXPECT_EQ(provided_birth_year, demographics_result.value().birth_year);
102   }
103 }
104 
TEST_F(UserDemographicsPrefsTest,ReadAndClearUserDemographicPreferences)105 TEST_F(UserDemographicsPrefsTest, ReadAndClearUserDemographicPreferences) {
106   // Verify demographic prefs are not available when there is nothing set.
107   ASSERT_FALSE(
108       GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), &pref_service_)
109           .IsSuccess());
110 
111   // Set demographic prefs directly from the pref service interface because
112   // demographic prefs will only be set on the server-side. The SyncPrefs
113   // interface cannot set demographic prefs.
114   SetDemographics(1983, UserDemographicsProto::GENDER_FEMALE);
115 
116   // Set birth year noise offset to not have it randomized.
117   pref_service_.SetInteger(kSyncDemographicsBirthYearOffsetPrefName, 2);
118 
119   // Verify that demographics are provided.
120   {
121     UserDemographicsResult demographics_result =
122         GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), &pref_service_);
123     ASSERT_TRUE(demographics_result.IsSuccess());
124   }
125 
126   ClearDemographicsPrefs(&pref_service_);
127 
128   // Verify that demographics are not provided and kSyncDemographics is cleared.
129   // Note that we retain kSyncDemographicsBirthYearOffset. If the user resumes
130   // syncing, causing these prefs to be recreated, we don't want them to start
131   // reporting a different randomized birth year as this could narrow down or
132   // even reveal their true birth year.
133   EXPECT_FALSE(
134       GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), &pref_service_)
135           .IsSuccess());
136   EXPECT_FALSE(pref_service_.HasPrefPath(kSyncDemographicsPrefName));
137   EXPECT_TRUE(
138       pref_service_.HasPrefPath(kSyncDemographicsBirthYearOffsetPrefName));
139 }
140 
141 struct DemographicsTestParam {
142   // Birth year of the user.
143   int birth_year = kUserDemographicsBirthYearDefaultValue;
144 
145   // Non-random offset to apply to |birth_year| as noise.
146   int birth_year_offset = kUserDemographicsBirthYearNoiseOffsetDefaultValue;
147 
148   // Gender of the user.
149   UserDemographicsProto_Gender gender = kUserDemographicGenderDefaultEnumValue;
150 
151   // Status of the retrieval of demographics.
152   UserDemographicsStatus status = UserDemographicsStatus::kMaxValue;
153 };
154 
155 // Extend UserDemographicsPrefsTest fixture for parameterized tests on
156 // demographics.
157 class UserDemographicsPrefsTestWithParam
158     : public UserDemographicsPrefsTest,
159       public testing::WithParamInterface<DemographicsTestParam> {};
160 
TEST_P(UserDemographicsPrefsTestWithParam,ReadDemographics_OffsetIsNotRandom)161 TEST_P(UserDemographicsPrefsTestWithParam, ReadDemographics_OffsetIsNotRandom) {
162   DemographicsTestParam param = GetParam();
163 
164   // Set user demographic prefs.
165   SetDemographics(param.birth_year, param.gender);
166 
167   // Set birth year noise offset to not have it randomized.
168   pref_service_.SetInteger(kSyncDemographicsBirthYearOffsetPrefName,
169                            param.birth_year_offset);
170 
171   // Verify provided demographics for the different parameterized test cases.
172   UserDemographicsResult demographics_result =
173       GetUserNoisedBirthYearAndGenderFromPrefs(GetNowTime(), &pref_service_);
174   if (param.status == UserDemographicsStatus::kSuccess) {
175     ASSERT_TRUE(demographics_result.IsSuccess());
176     EXPECT_EQ(param.birth_year + param.birth_year_offset,
177               demographics_result.value().birth_year);
178     EXPECT_EQ(param.gender, demographics_result.value().gender);
179   } else {
180     ASSERT_FALSE(demographics_result.IsSuccess());
181     EXPECT_EQ(param.status, demographics_result.status());
182   }
183 }
184 
185 // Test suite composed of different test cases of getting user demographics.
186 // The now time in each test case is "22 Jul 2019 00:00:00 UDT" which falls into
187 // the year bucket of 2018. Users need at most a |birth_year| +
188 // |birth_year_offset| of 1998 to be able to provide demographics.
189 INSTANTIATE_TEST_SUITE_P(
190     All,
191     UserDemographicsPrefsTestWithParam,
192     ::testing::Values(
193         // Test where birth year should not be provided because |birth_year| + 2
194         // > 1998.
195         DemographicsTestParam{
196             /*birth_year=*/1997,
197             /*birth_year_offset=*/2,
198             /*gender=*/UserDemographicsProto::GENDER_FEMALE,
199             /*status=*/UserDemographicsStatus::kIneligibleDemographicsData},
200         // Test where birth year should not be provided because |birth_year| - 2
201         // > 1998.
202         DemographicsTestParam{
203             /*birth_year=*/2001,
204             /*birth_year_offset=*/-2,
205             /*gender=*/UserDemographicsProto::GENDER_FEMALE,
206             /*status=*/UserDemographicsStatus::kIneligibleDemographicsData},
207         // Test where birth year should not be provided because age of user is
208         // |kUserDemographicsMaxAge| + 1, which is over the max age.
209         DemographicsTestParam{
210             /*birth_year=*/1933,
211             /*birth_year_offset=*/0,
212             /*gender=*/UserDemographicsProto::GENDER_FEMALE,
213             /*status=*/UserDemographicsStatus::kIneligibleDemographicsData},
214         // Test where gender should not be provided because it has a low
215         // population that can have their privacy compromised because of high
216         // entropy.
217         DemographicsTestParam{
218             /*birth_year=*/1986,
219             /*birth_year_offset=*/0,
220             /*gender=*/UserDemographicsProto::GENDER_CUSTOM_OR_OTHER,
221             /*status=*/UserDemographicsStatus::kIneligibleDemographicsData},
222         // Test where birth year can be provided because |birth_year| + 2 ==
223         // 1998.
224         DemographicsTestParam{/*birth_year=*/1996,
225                               /*birth_year_offset=*/2,
226                               /*gender=*/UserDemographicsProto::GENDER_FEMALE,
227                               /*status=*/UserDemographicsStatus::kSuccess},
228         // Test where birth year can be provided because |birth_year| - 2 ==
229         // 1998.
230         DemographicsTestParam{/*birth_year=*/2000,
231                               /*birth_year_offset=*/-2,
232                               /*gender=*/UserDemographicsProto::GENDER_MALE,
233                               /*status=*/UserDemographicsStatus::kSuccess},
234         // Test where birth year can be provided because |birth_year| + 2 <
235         // 1998.
236         DemographicsTestParam{/*birth_year=*/1995,
237                               /*birth_year_offset=*/2,
238                               /*gender=*/UserDemographicsProto::GENDER_FEMALE,
239                               /*status=*/UserDemographicsStatus::kSuccess},
240         // Test where birth year can be provided because |birth_year| - 2 <
241         // 1998.
242         DemographicsTestParam{/*birth_year=*/1999,
243                               /*birth_year_offset=*/-2,
244                               /*gender=*/UserDemographicsProto::GENDER_MALE,
245                               /*status=*/UserDemographicsStatus::kSuccess},
246         // Test where gender can be provided because it is part of a large
247         // population with a low entropy.
248         DemographicsTestParam{/*birth_year=*/1986,
249                               /*birth_year_offset=*/0,
250                               /*gender=*/UserDemographicsProto::GENDER_FEMALE,
251                               /*status=*/UserDemographicsStatus::kSuccess},
252         // Test where gender can be provided because it is part of a large
253         // population with a low entropy.
254         DemographicsTestParam{/*birth_year=*/1986,
255                               /*birth_year_offset=*/0,
256                               /*gender=*/UserDemographicsProto::GENDER_MALE,
257                               /*status=*/UserDemographicsStatus::kSuccess}));
258 
259 }  // namespace metrics
260