OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "base/base64.h" | |
6 #include "base/string_split.h" | |
7 #include "chrome/browser/metrics/proto/study.pb.h" | |
8 #include "chrome/browser/metrics/variations_service.h" | |
9 #include "chrome/common/chrome_version_info.h" | |
10 #include "chrome/common/pref_names.h" | |
11 #include "chrome/test/base/testing_pref_service.h" | |
12 #include "testing/gtest/include/gtest/gtest.h" | |
13 | |
14 namespace chrome_variations { | |
15 | |
16 namespace { | |
17 | |
18 // Converts |time| to Study proto format. | |
19 int64 TimeToProtoTime(const base::Time& time) { | |
20 return (time - base::Time::UnixEpoch()).InSeconds(); | |
21 } | |
22 | |
23 // Populates |seed| with simple test data. The resulting seed will contain one | |
24 // study called "test", which contains one experiment called "abc" with | |
25 // probability weight 100. |seed|'s study field will be cleared before adding | |
26 // the new study. | |
27 TrialsSeed CreateTestSeed() { | |
28 TrialsSeed seed; | |
29 Study* study = seed.add_study(); | |
30 study->set_name("test"); | |
31 study->set_default_experiment_name("abc"); | |
32 Study_Experiment* experiment = study->add_experiment(); | |
33 experiment->set_name("abc"); | |
34 experiment->set_probability_weight(100); | |
35 return seed; | |
36 } | |
37 | |
38 } // namespace | |
39 | |
40 TEST(VariationsServiceTest, CheckStudyChannel) { | |
41 const chrome::VersionInfo::Channel channels[] = { | |
42 chrome::VersionInfo::CHANNEL_CANARY, | |
43 chrome::VersionInfo::CHANNEL_DEV, | |
44 chrome::VersionInfo::CHANNEL_BETA, | |
45 chrome::VersionInfo::CHANNEL_STABLE, | |
46 }; | |
47 const Study_Channel study_channels[] = { | |
48 Study_Channel_CANARY, | |
49 Study_Channel_DEV, | |
50 Study_Channel_BETA, | |
51 Study_Channel_STABLE, | |
52 }; | |
53 ASSERT_EQ(arraysize(channels), arraysize(study_channels)); | |
54 bool channel_added[arraysize(channels)] = { 0 }; | |
55 | |
56 Study_Filter filter; | |
57 | |
58 // Check in the forwarded order. The loop cond is <= arraysize(study_channels) | |
59 // instead of < so that the result of adding the last channel gets checked. | |
60 for (size_t i = 0; i <= arraysize(study_channels); ++i) { | |
61 for (size_t j = 0; j < arraysize(channels); ++j) { | |
62 const bool expected = channel_added[j] || filter.channel_size() == 0; | |
63 const bool result = VariationsService::CheckStudyChannel(filter, | |
64 channels[j]); | |
65 EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!"; | |
66 } | |
67 | |
68 if (i < arraysize(study_channels)) { | |
69 filter.add_channel(study_channels[i]); | |
70 channel_added[i] = true; | |
71 } | |
72 } | |
73 | |
74 // Do the same check in the reverse order. | |
75 filter.clear_channel(); | |
76 memset(&channel_added, 0, sizeof(channel_added)); | |
77 for (size_t i = 0; i <= arraysize(study_channels); ++i) { | |
78 for (size_t j = 0; j < arraysize(channels); ++j) { | |
79 const bool expected = channel_added[j] || filter.channel_size() == 0; | |
80 const bool result = VariationsService::CheckStudyChannel(filter, | |
81 channels[j]); | |
82 EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!"; | |
83 } | |
84 | |
85 if (i < arraysize(study_channels)) { | |
86 const int index = arraysize(study_channels) - i - 1; | |
87 filter.add_channel(study_channels[index]); | |
88 channel_added[index] = true; | |
89 } | |
90 } | |
91 } | |
92 | |
93 TEST(VariationsServiceTest, CheckStudyLocale) { | |
94 struct { | |
95 const char* filter_locales; | |
96 bool en_us_result; | |
97 bool en_ca_result; | |
98 bool fr_result; | |
99 } test_cases[] = { | |
100 {"en-US", true, false, false}, | |
101 {"en-US,en-CA,fr", true, true, true}, | |
102 {"en-US,en-CA,en-GB", true, true, false}, | |
103 {"en-GB,en-CA,en-US", true, true, false}, | |
104 {"ja,kr,vi", false, false, false}, | |
105 {"fr-CA", false, false, false}, | |
106 {"", true, true, true}, | |
107 }; | |
108 | |
109 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { | |
110 std::vector<std::string> filter_locales; | |
111 Study_Filter filter; | |
112 base::SplitString(test_cases[i].filter_locales, ',', &filter_locales); | |
113 for (size_t j = 0; j < filter_locales.size(); ++j) | |
114 filter.add_locale(filter_locales[j]); | |
115 EXPECT_EQ(test_cases[i].en_us_result, | |
116 VariationsService::CheckStudyLocale(filter, "en-US")); | |
117 EXPECT_EQ(test_cases[i].en_ca_result, | |
118 VariationsService::CheckStudyLocale(filter, "en-CA")); | |
119 EXPECT_EQ(test_cases[i].fr_result, | |
120 VariationsService::CheckStudyLocale(filter, "fr")); | |
121 } | |
122 } | |
123 | |
124 TEST(VariationsServiceTest, CheckStudyPlatform) { | |
125 const Study_Platform platforms[] = { | |
126 Study_Platform_PLATFORM_WINDOWS, | |
127 Study_Platform_PLATFORM_MAC, | |
128 Study_Platform_PLATFORM_LINUX, | |
129 Study_Platform_PLATFORM_CHROMEOS, | |
130 Study_Platform_PLATFORM_ANDROID, | |
131 Study_Platform_PLATFORM_IOS, | |
132 }; | |
133 ASSERT_EQ(Study_Platform_Platform_ARRAYSIZE, | |
134 static_cast<int>(arraysize(platforms))); | |
135 bool platform_added[arraysize(platforms)] = { 0 }; | |
136 | |
137 Study_Filter filter; | |
138 | |
139 // Check in the forwarded order. The loop cond is <= arraysize(platforms) | |
140 // instead of < so that the result of adding the last channel gets checked. | |
141 for (size_t i = 0; i <= arraysize(platforms); ++i) { | |
142 for (size_t j = 0; j < arraysize(platforms); ++j) { | |
143 const bool expected = platform_added[j] || filter.platform_size() == 0; | |
144 const bool result = VariationsService::CheckStudyPlatform(filter, | |
145 platforms[j]); | |
146 EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!"; | |
147 } | |
148 | |
149 if (i < arraysize(platforms)) { | |
150 filter.add_platform(platforms[i]); | |
151 platform_added[i] = true; | |
152 } | |
153 } | |
154 | |
155 // Do the same check in the reverse order. | |
156 filter.clear_platform(); | |
157 memset(&platform_added, 0, sizeof(platform_added)); | |
158 for (size_t i = 0; i <= arraysize(platforms); ++i) { | |
159 for (size_t j = 0; j < arraysize(platforms); ++j) { | |
160 const bool expected = platform_added[j] || filter.platform_size() == 0; | |
161 const bool result = VariationsService::CheckStudyPlatform(filter, | |
162 platforms[j]); | |
163 EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!"; | |
164 } | |
165 | |
166 if (i < arraysize(platforms)) { | |
167 const int index = arraysize(platforms) - i - 1; | |
168 filter.add_platform(platforms[index]); | |
169 platform_added[index] = true; | |
170 } | |
171 } | |
172 } | |
173 | |
174 TEST(VariationsServiceTest, CheckStudyVersion) { | |
175 const struct { | |
176 const char* min_version; | |
177 const char* version; | |
178 bool expected_result; | |
179 } min_test_cases[] = { | |
180 { "1.2.2", "1.2.3", true }, | |
181 { "1.2.3", "1.2.3", true }, | |
182 { "1.2.4", "1.2.3", false }, | |
183 { "1.3.2", "1.2.3", false }, | |
184 { "2.1.2", "1.2.3", false }, | |
185 { "0.3.4", "1.2.3", true }, | |
186 // Wildcards. | |
187 { "1.*", "1.2.3", true }, | |
188 { "1.2.*", "1.2.3", true }, | |
189 { "1.2.3.*", "1.2.3", true }, | |
190 { "1.2.4.*", "1.2.3", false }, | |
191 { "2.*", "1.2.3", false }, | |
192 { "0.3.*", "1.2.3", true }, | |
193 }; | |
194 | |
195 const struct { | |
196 const char* max_version; | |
197 const char* version; | |
198 bool expected_result; | |
199 } max_test_cases[] = { | |
200 { "1.2.2", "1.2.3", false }, | |
201 { "1.2.3", "1.2.3", true }, | |
202 { "1.2.4", "1.2.3", true }, | |
203 { "2.1.1", "1.2.3", true }, | |
204 { "2.1.1", "2.3.4", false }, | |
205 // Wildcards | |
206 { "2.1.*", "2.3.4", false }, | |
207 { "2.*", "2.3.4", true }, | |
208 { "2.3.*", "2.3.4", true }, | |
209 { "2.3.4.*", "2.3.4", true }, | |
210 { "2.3.4.0.*", "2.3.4", true }, | |
211 { "2.4.*", "2.3.4", true }, | |
212 { "1.3.*", "2.3.4", false }, | |
213 { "1.*", "2.3.4", false }, | |
214 }; | |
215 | |
216 Study_Filter filter; | |
217 | |
218 // Min/max version not set should result in true. | |
219 EXPECT_TRUE(VariationsService::CheckStudyVersion(filter, "1.2.3")); | |
220 | |
221 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) { | |
222 filter.set_min_version(min_test_cases[i].min_version); | |
223 const bool result = | |
224 VariationsService::CheckStudyVersion(filter, min_test_cases[i].version); | |
225 EXPECT_EQ(min_test_cases[i].expected_result, result) << | |
226 "Min. version case " << i << " failed!"; | |
227 } | |
228 filter.clear_min_version(); | |
229 | |
230 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(max_test_cases); ++i) { | |
231 filter.set_max_version(max_test_cases[i].max_version); | |
232 const bool result = | |
233 VariationsService::CheckStudyVersion(filter, max_test_cases[i].version); | |
234 EXPECT_EQ(max_test_cases[i].expected_result, result) << | |
235 "Max version case " << i << " failed!"; | |
236 } | |
237 | |
238 // Check intersection semantics. | |
239 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) { | |
240 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(max_test_cases); ++j) { | |
241 filter.set_min_version(min_test_cases[i].min_version); | |
242 filter.set_max_version(max_test_cases[j].max_version); | |
243 | |
244 if (!min_test_cases[i].expected_result) { | |
245 const bool result = | |
246 VariationsService::CheckStudyVersion(filter, | |
247 min_test_cases[i].version); | |
248 EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!"; | |
249 } | |
250 | |
251 if (!max_test_cases[j].expected_result) { | |
252 const bool result = | |
253 VariationsService::CheckStudyVersion(filter, | |
254 max_test_cases[j].version); | |
255 EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!"; | |
256 } | |
257 } | |
258 } | |
259 } | |
260 | |
261 TEST(VariationsServiceTest, CheckStudyStartDate) { | |
262 const base::Time now = base::Time::Now(); | |
263 const base::TimeDelta delta = base::TimeDelta::FromHours(1); | |
264 const struct { | |
265 const base::Time start_date; | |
266 bool expected_result; | |
267 } start_test_cases[] = { | |
268 { now - delta, true }, | |
269 { now, true }, | |
270 { now + delta, false }, | |
271 }; | |
272 | |
273 Study_Filter filter; | |
274 | |
275 // Start date not set should result in true. | |
276 EXPECT_TRUE(VariationsService::CheckStudyStartDate(filter, now)); | |
277 | |
278 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(start_test_cases); ++i) { | |
279 filter.set_start_date(TimeToProtoTime(start_test_cases[i].start_date)); | |
280 const bool result = VariationsService::CheckStudyStartDate(filter, now); | |
281 EXPECT_EQ(start_test_cases[i].expected_result, result) | |
282 << "Case " << i << " failed!"; | |
283 } | |
284 } | |
285 | |
286 TEST(VariationsServiceTest, IsStudyExpired) { | |
287 const base::Time now = base::Time::Now(); | |
288 const base::TimeDelta delta = base::TimeDelta::FromHours(1); | |
289 const struct { | |
290 const base::Time expiry_date; | |
291 bool expected_result; | |
292 } expiry_test_cases[] = { | |
293 { now - delta, true }, | |
294 { now, true }, | |
295 { now + delta, false }, | |
296 }; | |
297 | |
298 Study study; | |
299 | |
300 // Expiry date not set should result in false. | |
301 EXPECT_FALSE(VariationsService::IsStudyExpired(study, now)); | |
302 | |
303 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(expiry_test_cases); ++i) { | |
304 study.set_expiry_date(TimeToProtoTime(expiry_test_cases[i].expiry_date)); | |
305 const bool result = VariationsService::IsStudyExpired(study, now); | |
306 EXPECT_EQ(expiry_test_cases[i].expected_result, result) | |
307 << "Case " << i << " failed!"; | |
308 } | |
309 } | |
310 | |
311 TEST(VariationsServiceTest, LoadSeed) { | |
312 TestingPrefService pref_service; | |
313 | |
314 VariationsService::RegisterPrefs(&pref_service); | |
315 | |
316 // Store good seed data to test if loading from prefs works. | |
317 TrialsSeed seed = CreateTestSeed(); | |
318 | |
319 std::string serialized_seed; | |
320 seed.SerializeToString(&serialized_seed); | |
321 std::string base64_serialized_seed; | |
322 ASSERT_TRUE(base::Base64Encode(serialized_seed, &base64_serialized_seed)); | |
323 pref_service.SetString(prefs::kVariationsSeed, base64_serialized_seed); | |
324 | |
325 VariationsService variations_service; | |
326 TrialsSeed loaded_seed; | |
327 EXPECT_TRUE( | |
328 variations_service.LoadTrialsSeedFromPref(&pref_service, &loaded_seed)); | |
329 | |
330 std::string serialized_loaded_seed; | |
331 loaded_seed.SerializeToString(&serialized_loaded_seed); | |
332 // Check that the loaded data is the same as the original. | |
333 EXPECT_EQ(serialized_seed, serialized_loaded_seed); | |
334 // Make sure the pref hasn't been changed. | |
335 EXPECT_FALSE( | |
336 pref_service.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); | |
337 EXPECT_EQ(base64_serialized_seed, | |
338 pref_service.GetString(prefs::kVariationsSeed)); | |
339 | |
340 // Check that loading a bad seed returns false and clears the pref. | |
341 pref_service.ClearPref(prefs::kVariationsSeed); | |
342 pref_service.SetString(prefs::kVariationsSeed, "this should fail"); | |
343 EXPECT_FALSE( | |
344 pref_service.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); | |
345 EXPECT_FALSE( | |
346 variations_service.LoadTrialsSeedFromPref(&pref_service, &loaded_seed)); | |
347 EXPECT_TRUE( | |
348 pref_service.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); | |
349 } | |
350 | |
351 TEST(VariationsServiceTest, StoreSeed) { | |
352 TestingPrefService pref_service; | |
353 | |
354 VariationsService::RegisterPrefs(&pref_service); | |
355 const base::Time now = base::Time::Now(); | |
356 | |
357 TrialsSeed seed = CreateTestSeed(); | |
358 | |
359 VariationsService variations_service; | |
360 std::string serialized_seed; | |
361 seed.SerializeToString(&serialized_seed); | |
362 EXPECT_TRUE( | |
363 variations_service.StoreSeedData(serialized_seed, now, &pref_service)); | |
364 // Make sure the pref was actually set. | |
365 EXPECT_FALSE( | |
366 pref_service.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); | |
367 | |
368 std::string loaded_serialized_seed = | |
369 pref_service.GetString(prefs::kVariationsSeed); | |
370 std::string decoded_serialized_seed; | |
371 ASSERT_TRUE(base::Base64Decode(loaded_serialized_seed, | |
372 &decoded_serialized_seed)); | |
373 // Make sure the stored seed from pref is the same as the seed we created. | |
374 EXPECT_EQ(serialized_seed, decoded_serialized_seed); | |
375 | |
376 // Check if trying to store a bad seed leaves the pref unchanged. | |
377 pref_service.ClearPref(prefs::kVariationsSeed); | |
378 EXPECT_FALSE( | |
379 variations_service.StoreSeedData("this should fail", now, &pref_service)); | |
380 EXPECT_TRUE( | |
381 pref_service.FindPreference(prefs::kVariationsSeed)->IsDefaultValue()); | |
382 } | |
383 | |
384 TEST(VariationsServiceTest, ValidateStudy) { | |
385 Study study; | |
386 study.set_default_experiment_name("def"); | |
387 | |
388 Study_Experiment* experiment = study.add_experiment(); | |
389 experiment->set_name("abc"); | |
390 experiment->set_probability_weight(100); | |
391 | |
392 Study_Experiment* default_group = study.add_experiment(); | |
393 default_group->set_name("def"); | |
394 default_group->set_probability_weight(200); | |
395 | |
396 base::FieldTrial::Probability total_probability = 0; | |
397 bool valid = VariationsService::ValidateStudyAndComputeTotalProbability( | |
398 study, &total_probability); | |
399 EXPECT_TRUE(valid); | |
400 EXPECT_EQ(300, total_probability); | |
401 | |
402 // Min version checks. | |
403 study.mutable_filter()->set_min_version("1.2.3.*"); | |
404 valid = VariationsService::ValidateStudyAndComputeTotalProbability( | |
405 study, &total_probability); | |
406 EXPECT_TRUE(valid); | |
407 study.mutable_filter()->set_min_version("1.*.3"); | |
408 valid = VariationsService::ValidateStudyAndComputeTotalProbability( | |
409 study, &total_probability); | |
410 EXPECT_FALSE(valid); | |
411 study.mutable_filter()->set_min_version("1.2.3"); | |
412 valid = VariationsService::ValidateStudyAndComputeTotalProbability( | |
413 study, &total_probability); | |
414 EXPECT_TRUE(valid); | |
415 | |
416 // Max version checks. | |
417 study.mutable_filter()->set_max_version("2.3.4.*"); | |
418 valid = VariationsService::ValidateStudyAndComputeTotalProbability( | |
419 study, &total_probability); | |
420 EXPECT_TRUE(valid); | |
421 study.mutable_filter()->set_max_version("*.3"); | |
422 valid = VariationsService::ValidateStudyAndComputeTotalProbability( | |
423 study, &total_probability); | |
424 EXPECT_FALSE(valid); | |
425 study.mutable_filter()->set_max_version("2.3.4"); | |
426 valid = VariationsService::ValidateStudyAndComputeTotalProbability( | |
427 study, &total_probability); | |
428 EXPECT_TRUE(valid); | |
429 | |
430 study.clear_default_experiment_name(); | |
431 valid = VariationsService::ValidateStudyAndComputeTotalProbability(study, | |
432 &total_probability); | |
433 EXPECT_FALSE(valid); | |
434 | |
435 study.set_default_experiment_name("xyz"); | |
436 valid = VariationsService::ValidateStudyAndComputeTotalProbability(study, | |
437 &total_probability); | |
438 EXPECT_FALSE(valid); | |
439 | |
440 study.set_default_experiment_name("def"); | |
441 default_group->clear_name(); | |
442 valid = VariationsService::ValidateStudyAndComputeTotalProbability(study, | |
443 &total_probability); | |
444 EXPECT_FALSE(valid); | |
445 | |
446 default_group->set_name("def"); | |
447 valid = VariationsService::ValidateStudyAndComputeTotalProbability(study, | |
448 &total_probability); | |
449 ASSERT_TRUE(valid); | |
450 Study_Experiment* repeated_group = study.add_experiment(); | |
451 repeated_group->set_name("abc"); | |
452 repeated_group->set_probability_weight(1); | |
453 valid = VariationsService::ValidateStudyAndComputeTotalProbability(study, | |
454 &total_probability); | |
455 EXPECT_FALSE(valid); | |
456 } | |
457 | |
458 } // namespace chrome_variations | |
OLD | NEW |