blob: f41d36ecc39da76f8edb769295d4c522e33b64af [file] [log] [blame]
[email protected]1871a1702013-07-26 09:37:431// Copyright 2013 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
[email protected]50ae9f12013-08-29 18:03:225#include "components/variations/variations_seed_processor.h"
[email protected]1871a1702013-07-26 09:37:436
avi5dd91f82015-12-25 22:30:467#include <stddef.h>
8#include <stdint.h>
dcheng3f767dc32016-04-25 22:54:229
[email protected]e24fff362014-07-22 01:19:0210#include <map>
dcheng3f767dc32016-04-25 22:54:2211#include <memory>
dcheng51ace48a2015-12-26 22:45:1712#include <utility>
[email protected]1871a1702013-07-26 09:37:4313#include <vector>
14
[email protected]e24fff362014-07-22 01:19:0215#include "base/bind.h"
[email protected]1871a1702013-07-26 09:37:4316#include "base/command_line.h"
asvitkine8423d172015-09-28 23:23:4417#include "base/feature_list.h"
18#include "base/format_macros.h"
avi5dd91f82015-12-25 22:30:4619#include "base/macros.h"
Byoungkown1bb50222018-09-11 01:14:4120#include "base/stl_util.h"
[email protected]1871a1702013-07-26 09:37:4321#include "base/strings/string_split.h"
asvitkine8423d172015-09-28 23:23:4422#include "base/strings/stringprintf.h"
[email protected]e24fff362014-07-22 01:19:0223#include "base/strings/utf_string_conversions.h"
jwd67c08f752016-05-18 21:04:5924#include "base/test/mock_entropy_provider.h"
asvitkine9499b8d2016-08-09 05:37:0725#include "base/test/scoped_feature_list.h"
isherman52a81bd2017-06-07 23:30:0626#include "components/variations/client_filterable_state.h"
[email protected]d4f84852013-11-08 01:05:3527#include "components/variations/processed_study.h"
asvitkine71411062016-05-17 20:09:4428#include "components/variations/study_filtering.h"
[email protected]50ae9f12013-08-29 18:03:2229#include "components/variations/variations_associated_data.h"
Alexei Svitkine5bbc9152018-02-27 16:02:3030#include "testing/gmock/include/gmock/gmock.h"
[email protected]1871a1702013-07-26 09:37:4331#include "testing/gtest/include/gtest/gtest.h"
32
Alexei Svitkine5bbc9152018-02-27 16:02:3033using testing::ElementsAre;
34using testing::IsEmpty;
35
[email protected]59b6f672014-07-26 18:35:4736namespace variations {
[email protected]1871a1702013-07-26 09:37:4337namespace {
38
39// Converts |time| to Study proto format.
avi5dd91f82015-12-25 22:30:4640int64_t TimeToProtoTime(const base::Time& time) {
[email protected]1871a1702013-07-26 09:37:4341 return (time - base::Time::UnixEpoch()).InSeconds();
42}
43
44// Constants for testing associating command line flags with trial groups.
45const char kFlagStudyName[] = "flag_test_trial";
46const char kFlagGroup1Name[] = "flag_group1";
47const char kFlagGroup2Name[] = "flag_group2";
48const char kNonFlagGroupName[] = "non_flag_group";
jkrcal7ce02462016-07-01 09:57:5349const char kOtherGroupName[] = "other_group";
[email protected]1871a1702013-07-26 09:37:4350const char kForcingFlag1[] = "flag_test1";
51const char kForcingFlag2[] = "flag_test2";
52
[email protected]0aafa3e2013-11-18 09:32:4553const VariationID kExperimentId = 123;
54
[email protected]5bd00ce2013-08-05 23:46:1255// Adds an experiment to |study| with the specified |name| and |probability|.
56Study_Experiment* AddExperiment(const std::string& name, int probability,
57 Study* study) {
58 Study_Experiment* experiment = study->add_experiment();
59 experiment->set_name(name);
60 experiment->set_probability_weight(probability);
61 return experiment;
62}
63
[email protected]1871a1702013-07-26 09:37:4364// Populates |study| with test data used for testing associating command line
65// flags with trials groups. The study will contain three groups, a default
66// group that isn't associated with a flag, and two other groups, both
67// associated with different flags.
68Study CreateStudyWithFlagGroups(int default_group_probability,
69 int flag_group1_probability,
70 int flag_group2_probability) {
71 DCHECK_GE(default_group_probability, 0);
72 DCHECK_GE(flag_group1_probability, 0);
73 DCHECK_GE(flag_group2_probability, 0);
74 Study study;
75 study.set_name(kFlagStudyName);
76 study.set_default_experiment_name(kNonFlagGroupName);
77
[email protected]5bd00ce2013-08-05 23:46:1278 AddExperiment(kNonFlagGroupName, default_group_probability, &study);
79 AddExperiment(kFlagGroup1Name, flag_group1_probability, &study)
80 ->set_forcing_flag(kForcingFlag1);
81 AddExperiment(kFlagGroup2Name, flag_group2_probability, &study)
82 ->set_forcing_flag(kForcingFlag2);
[email protected]1871a1702013-07-26 09:37:4383
84 return study;
85}
86
[email protected]e24fff362014-07-22 01:19:0287class TestOverrideStringCallback {
88 public:
89 typedef std::map<uint32_t, base::string16> OverrideMap;
90
91 TestOverrideStringCallback()
92 : callback_(base::Bind(&TestOverrideStringCallback::Override,
93 base::Unretained(this))) {}
94
95 virtual ~TestOverrideStringCallback() {}
96
97 const VariationsSeedProcessor::UIStringOverrideCallback& callback() const {
98 return callback_;
99 }
100
101 const OverrideMap& overrides() const { return overrides_; }
102
103 private:
104 void Override(uint32_t hash, const base::string16& string) {
105 overrides_[hash] = string;
106 }
107
108 VariationsSeedProcessor::UIStringOverrideCallback callback_;
109 OverrideMap overrides_;
110
111 DISALLOW_COPY_AND_ASSIGN(TestOverrideStringCallback);
112};
113
[email protected]1871a1702013-07-26 09:37:43114} // namespace
115
[email protected]f2fc7cf42013-11-07 21:53:03116class VariationsSeedProcessorTest : public ::testing::Test {
117 public:
118 VariationsSeedProcessorTest() {
119 }
120
dcheng30a1b1542014-10-29 21:27:50121 ~VariationsSeedProcessorTest() override {
[email protected]f2fc7cf42013-11-07 21:53:03122 // Ensure that the maps are cleared between tests, since they are stored as
123 // process singletons.
124 testing::ClearAllVariationIDs();
125 testing::ClearAllVariationParams();
126 }
127
asvitkine71411062016-05-17 20:09:44128 bool CreateTrialFromStudy(const Study& study) {
jwd67c08f752016-05-18 21:04:59129 return CreateTrialFromStudyWithFeatureListAndEntropyOverride(
130 study, nullptr, &feature_list_);
131 }
132
133 bool CreateTrialFromStudyWithEntropyOverride(
134 const Study& study,
135 const base::FieldTrial::EntropyProvider* override_entropy_provider) {
136 return CreateTrialFromStudyWithFeatureListAndEntropyOverride(
137 study, override_entropy_provider, &feature_list_);
asvitkine8423d172015-09-28 23:23:44138 }
139
asvitkine71411062016-05-17 20:09:44140 bool CreateTrialFromStudyWithFeatureList(const Study& study,
asvitkine8423d172015-09-28 23:23:44141 base::FeatureList* feature_list) {
jwd67c08f752016-05-18 21:04:59142 return CreateTrialFromStudyWithFeatureListAndEntropyOverride(study, nullptr,
143 feature_list);
144 }
145
146 bool CreateTrialFromStudyWithFeatureListAndEntropyOverride(
147 const Study& study,
148 const base::FieldTrial::EntropyProvider* override_entropy_provider,
149 base::FeatureList* feature_list) {
[email protected]70fbd0052013-11-20 02:22:06150 ProcessedStudy processed_study;
asvitkine71411062016-05-17 20:09:44151 const bool is_expired = internal::IsStudyExpired(study, base::Time::Now());
152 if (processed_study.Init(&study, is_expired)) {
[email protected]e24fff362014-07-22 01:19:02153 VariationsSeedProcessor().CreateTrialFromStudy(
jwd67c08f752016-05-18 21:04:59154 processed_study, override_callback_.callback(),
155 override_entropy_provider, feature_list);
[email protected]70fbd0052013-11-20 02:22:06156 return true;
157 }
158 return false;
159 }
160
[email protected]e24fff362014-07-22 01:19:02161 protected:
asvitkine8423d172015-09-28 23:23:44162 base::FeatureList feature_list_;
[email protected]e24fff362014-07-22 01:19:02163 TestOverrideStringCallback override_callback_;
164
[email protected]f2fc7cf42013-11-07 21:53:03165 private:
166 DISALLOW_COPY_AND_ASSIGN(VariationsSeedProcessorTest);
167};
168
[email protected]0aafa3e2013-11-18 09:32:45169TEST_F(VariationsSeedProcessorTest, AllowForceGroupAndVariationId) {
avi1772c1a2014-12-22 22:42:33170 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
[email protected]0aafa3e2013-11-18 09:32:45171
asvitkine71411062016-05-17 20:09:44172 base::FieldTrialList field_trial_list(nullptr);
[email protected]0aafa3e2013-11-18 09:32:45173
174 Study study = CreateStudyWithFlagGroups(100, 0, 0);
175 study.mutable_experiment(1)->set_google_web_experiment_id(kExperimentId);
[email protected]0aafa3e2013-11-18 09:32:45176
asvitkine71411062016-05-17 20:09:44177 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]0aafa3e2013-11-18 09:32:45178 EXPECT_EQ(kFlagGroup1Name,
179 base::FieldTrialList::FindFullName(kFlagStudyName));
180
181 VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName,
182 kFlagGroup1Name);
183 EXPECT_EQ(kExperimentId, id);
184}
185
[email protected]1871a1702013-07-26 09:37:43186// Test that the group for kForcingFlag1 is forced.
[email protected]f2fc7cf42013-11-07 21:53:03187TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag1) {
avi1772c1a2014-12-22 22:42:33188 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
[email protected]1871a1702013-07-26 09:37:43189
asvitkine71411062016-05-17 20:09:44190 base::FieldTrialList field_trial_list(nullptr);
[email protected]1871a1702013-07-26 09:37:43191
192 Study study = CreateStudyWithFlagGroups(100, 0, 0);
asvitkine71411062016-05-17 20:09:44193 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]1871a1702013-07-26 09:37:43194 EXPECT_EQ(kFlagGroup1Name,
195 base::FieldTrialList::FindFullName(kFlagStudyName));
196}
197
198// Test that the group for kForcingFlag2 is forced.
[email protected]f2fc7cf42013-11-07 21:53:03199TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag2) {
avi1772c1a2014-12-22 22:42:33200 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
[email protected]1871a1702013-07-26 09:37:43201
asvitkine71411062016-05-17 20:09:44202 base::FieldTrialList field_trial_list(nullptr);
[email protected]1871a1702013-07-26 09:37:43203
204 Study study = CreateStudyWithFlagGroups(100, 0, 0);
asvitkine71411062016-05-17 20:09:44205 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]1871a1702013-07-26 09:37:43206 EXPECT_EQ(kFlagGroup2Name,
207 base::FieldTrialList::FindFullName(kFlagStudyName));
208}
209
[email protected]f2fc7cf42013-11-07 21:53:03210TEST_F(VariationsSeedProcessorTest, ForceGroup_ChooseFirstGroupWithFlag) {
[email protected]1871a1702013-07-26 09:37:43211 // Add the flag to the command line arguments so the flag group is forced.
avi1772c1a2014-12-22 22:42:33212 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
213 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
[email protected]1871a1702013-07-26 09:37:43214
asvitkine71411062016-05-17 20:09:44215 base::FieldTrialList field_trial_list(nullptr);
[email protected]1871a1702013-07-26 09:37:43216
217 Study study = CreateStudyWithFlagGroups(100, 0, 0);
asvitkine71411062016-05-17 20:09:44218 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]1871a1702013-07-26 09:37:43219 EXPECT_EQ(kFlagGroup1Name,
220 base::FieldTrialList::FindFullName(kFlagStudyName));
221}
222
[email protected]f2fc7cf42013-11-07 21:53:03223TEST_F(VariationsSeedProcessorTest, ForceGroup_DontChooseGroupWithFlag) {
asvitkine71411062016-05-17 20:09:44224 base::FieldTrialList field_trial_list(nullptr);
[email protected]1871a1702013-07-26 09:37:43225
226 // The two flag groups are given high probability, which would normally make
227 // them very likely to be chosen. They won't be chosen since flag groups are
228 // never chosen when their flag isn't present.
229 Study study = CreateStudyWithFlagGroups(1, 999, 999);
asvitkine71411062016-05-17 20:09:44230 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]1871a1702013-07-26 09:37:43231 EXPECT_EQ(kNonFlagGroupName,
232 base::FieldTrialList::FindFullName(kFlagStudyName));
233}
234
jkrcal7ce02462016-07-01 09:57:53235TEST_F(VariationsSeedProcessorTest, CreateTrialForRegisteredGroup) {
236 base::FieldTrialList field_trial_list(nullptr);
237
238 base::FieldTrialList::CreateFieldTrial(kFlagStudyName, kOtherGroupName);
239
240 // Create an arbitrary study that does not have group named |kOtherGroupName|.
241 Study study = CreateStudyWithFlagGroups(100, 0, 0);
242 // Creating the trial should not crash.
243 EXPECT_TRUE(CreateTrialFromStudy(study));
244 // And the previous group should still be selected.
245 EXPECT_EQ(kOtherGroupName,
246 base::FieldTrialList::FindFullName(kFlagStudyName));
247}
248
[email protected]f2fc7cf42013-11-07 21:53:03249TEST_F(VariationsSeedProcessorTest,
250 NonExpiredStudyPrioritizedOverExpiredStudy) {
[email protected]5bd00ce2013-08-05 23:46:12251 VariationsSeedProcessor seed_processor;
252
253 const std::string kTrialName = "A";
254 const std::string kGroup1Name = "Group1";
255
[email protected]541f66e2013-09-03 15:00:15256 VariationsSeed seed;
[email protected]5bd00ce2013-08-05 23:46:12257 Study* study1 = seed.add_study();
258 study1->set_name(kTrialName);
259 study1->set_default_experiment_name("Default");
260 AddExperiment(kGroup1Name, 100, study1);
261 AddExperiment("Default", 0, study1);
262 Study* study2 = seed.add_study();
263 *study2 = *study1;
264 ASSERT_EQ(seed.study(0).name(), seed.study(1).name());
265
266 const base::Time year_ago =
267 base::Time::Now() - base::TimeDelta::FromDays(365);
268
isherman52a81bd2017-06-07 23:30:06269 ClientFilterableState client_state;
270 client_state.locale = "en-CA";
271 client_state.reference_date = base::Time::Now();
272 client_state.version = base::Version("20.0.0.0");
273 client_state.channel = Study::STABLE;
274 client_state.form_factor = Study::DESKTOP;
275 client_state.platform = Study::PLATFORM_ANDROID;
[email protected]12a9b5552013-08-09 11:05:50276
[email protected]5bd00ce2013-08-05 23:46:12277 // Check that adding [expired, non-expired] activates the non-expired one.
278 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
279 {
asvitkine8423d172015-09-28 23:23:44280 base::FeatureList feature_list;
asvitkine71411062016-05-17 20:09:44281 base::FieldTrialList field_trial_list(nullptr);
[email protected]5bd00ce2013-08-05 23:46:12282 study1->set_expiry_date(TimeToProtoTime(year_ago));
isherman52a81bd2017-06-07 23:30:06283 seed_processor.CreateTrialsFromSeed(seed, client_state,
284 override_callback_.callback(), nullptr,
285 &feature_list);
[email protected]5bd00ce2013-08-05 23:46:12286 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
287 }
288
289 // Check that adding [non-expired, expired] activates the non-expired one.
290 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
291 {
asvitkine8423d172015-09-28 23:23:44292 base::FeatureList feature_list;
asvitkine71411062016-05-17 20:09:44293 base::FieldTrialList field_trial_list(nullptr);
[email protected]5bd00ce2013-08-05 23:46:12294 study1->clear_expiry_date();
295 study2->set_expiry_date(TimeToProtoTime(year_ago));
isherman52a81bd2017-06-07 23:30:06296 seed_processor.CreateTrialsFromSeed(seed, client_state,
297 override_callback_.callback(), nullptr,
298 &feature_list);
[email protected]5bd00ce2013-08-05 23:46:12299 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
300 }
301}
302
[email protected]e24fff362014-07-22 01:19:02303TEST_F(VariationsSeedProcessorTest, OverrideUIStrings) {
asvitkine71411062016-05-17 20:09:44304 base::FieldTrialList field_trial_list(nullptr);
[email protected]e24fff362014-07-22 01:19:02305
306 Study study;
307 study.set_name("Study1");
308 study.set_default_experiment_name("B");
Steven Holte93e681c2019-01-30 00:33:01309 study.set_activation_type(Study_ActivationType_ACTIVATE_ON_STARTUP);
[email protected]e24fff362014-07-22 01:19:02310
311 Study_Experiment* experiment1 = AddExperiment("A", 0, &study);
312 Study_Experiment_OverrideUIString* override =
313 experiment1->add_override_ui_string();
314
315 override->set_name_hash(1234);
316 override->set_value("test");
317
318 Study_Experiment* experiment2 = AddExperiment("B", 1, &study);
319
asvitkine71411062016-05-17 20:09:44320 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]e24fff362014-07-22 01:19:02321
322 const TestOverrideStringCallback::OverrideMap& overrides =
323 override_callback_.overrides();
324
325 EXPECT_TRUE(overrides.empty());
326
327 study.set_name("Study2");
328 experiment1->set_probability_weight(1);
329 experiment2->set_probability_weight(0);
330
asvitkine71411062016-05-17 20:09:44331 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]e24fff362014-07-22 01:19:02332
333 EXPECT_EQ(1u, overrides.size());
jdoerrie3feb1852018-10-05 12:16:44334 auto it = overrides.find(1234);
[email protected]e24fff362014-07-22 01:19:02335 EXPECT_EQ(base::ASCIIToUTF16("test"), it->second);
336}
337
338TEST_F(VariationsSeedProcessorTest, OverrideUIStringsWithForcingFlag) {
339 Study study = CreateStudyWithFlagGroups(100, 0, 0);
340 ASSERT_EQ(kForcingFlag1, study.experiment(1).forcing_flag());
341
Steven Holte93e681c2019-01-30 00:33:01342 study.set_activation_type(Study_ActivationType_ACTIVATE_ON_STARTUP);
[email protected]e24fff362014-07-22 01:19:02343 Study_Experiment_OverrideUIString* override =
344 study.mutable_experiment(1)->add_override_ui_string();
345 override->set_name_hash(1234);
346 override->set_value("test");
347
avi1772c1a2014-12-22 22:42:33348 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
asvitkine71411062016-05-17 20:09:44349 base::FieldTrialList field_trial_list(nullptr);
350 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]e24fff362014-07-22 01:19:02351 EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study.name()));
352
353 const TestOverrideStringCallback::OverrideMap& overrides =
354 override_callback_.overrides();
355 EXPECT_EQ(1u, overrides.size());
jdoerrie3feb1852018-10-05 12:16:44356 auto it = overrides.find(1234);
[email protected]e24fff362014-07-22 01:19:02357 EXPECT_EQ(base::ASCIIToUTF16("test"), it->second);
358}
359
[email protected]f2fc7cf42013-11-07 21:53:03360TEST_F(VariationsSeedProcessorTest, ValidateStudy) {
[email protected]1871a1702013-07-26 09:37:43361 Study study;
362 study.set_default_experiment_name("def");
[email protected]5bd00ce2013-08-05 23:46:12363 AddExperiment("abc", 100, &study);
364 Study_Experiment* default_group = AddExperiment("def", 200, &study);
[email protected]1871a1702013-07-26 09:37:43365
[email protected]70fbd0052013-11-20 02:22:06366 ProcessedStudy processed_study;
367 EXPECT_TRUE(processed_study.Init(&study, false));
368 EXPECT_EQ(300, processed_study.total_probability());
asvitkine30ac09132015-02-20 19:50:10369 EXPECT_FALSE(processed_study.all_assignments_to_one_group());
[email protected]1871a1702013-07-26 09:37:43370
371 // Min version checks.
372 study.mutable_filter()->set_min_version("1.2.3.*");
[email protected]70fbd0052013-11-20 02:22:06373 EXPECT_TRUE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43374 study.mutable_filter()->set_min_version("1.*.3");
[email protected]70fbd0052013-11-20 02:22:06375 EXPECT_FALSE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43376 study.mutable_filter()->set_min_version("1.2.3");
[email protected]70fbd0052013-11-20 02:22:06377 EXPECT_TRUE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43378
379 // Max version checks.
380 study.mutable_filter()->set_max_version("2.3.4.*");
[email protected]70fbd0052013-11-20 02:22:06381 EXPECT_TRUE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43382 study.mutable_filter()->set_max_version("*.3");
[email protected]70fbd0052013-11-20 02:22:06383 EXPECT_FALSE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43384 study.mutable_filter()->set_max_version("2.3.4");
[email protected]70fbd0052013-11-20 02:22:06385 EXPECT_TRUE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43386
jwd5ff54102017-01-06 18:09:34387 // A blank default study is allowed.
[email protected]1871a1702013-07-26 09:37:43388 study.clear_default_experiment_name();
jwd5ff54102017-01-06 18:09:34389 EXPECT_TRUE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43390
391 study.set_default_experiment_name("xyz");
[email protected]70fbd0052013-11-20 02:22:06392 EXPECT_FALSE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43393
394 study.set_default_experiment_name("def");
395 default_group->clear_name();
[email protected]70fbd0052013-11-20 02:22:06396 EXPECT_FALSE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43397
398 default_group->set_name("def");
[email protected]70fbd0052013-11-20 02:22:06399 EXPECT_TRUE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43400 Study_Experiment* repeated_group = study.add_experiment();
401 repeated_group->set_name("abc");
402 repeated_group->set_probability_weight(1);
[email protected]70fbd0052013-11-20 02:22:06403 EXPECT_FALSE(processed_study.Init(&study, false));
[email protected]1871a1702013-07-26 09:37:43404}
405
Alexei Svitkinec808b532018-03-15 23:53:38406TEST_F(VariationsSeedProcessorTest, ValidateStudyWithAssociatedFeatures) {
asvitkine64e9e112016-03-17 17:32:00407 Study study;
408 study.set_default_experiment_name("def");
409 Study_Experiment* exp1 = AddExperiment("exp1", 100, &study);
410 Study_Experiment* exp2 = AddExperiment("exp2", 100, &study);
411 Study_Experiment* exp3 = AddExperiment("exp3", 100, &study);
412 AddExperiment("def", 100, &study);
413
414 ProcessedStudy processed_study;
415 EXPECT_TRUE(processed_study.Init(&study, false));
416 EXPECT_EQ(400, processed_study.total_probability());
417
Alexei Svitkine5bbc9152018-02-27 16:02:30418 EXPECT_THAT(processed_study.associated_features(), IsEmpty());
asvitkine64e9e112016-03-17 17:32:00419
420 const char kFeature1Name[] = "Feature1";
421 const char kFeature2Name[] = "Feature2";
422
423 exp1->mutable_feature_association()->add_enable_feature(kFeature1Name);
424 EXPECT_TRUE(processed_study.Init(&study, false));
Alexei Svitkine5bbc9152018-02-27 16:02:30425 EXPECT_THAT(processed_study.associated_features(),
426 ElementsAre(kFeature1Name));
asvitkine64e9e112016-03-17 17:32:00427
428 exp1->clear_feature_association();
429 exp1->mutable_feature_association()->add_enable_feature(kFeature1Name);
430 exp1->mutable_feature_association()->add_enable_feature(kFeature2Name);
431 EXPECT_TRUE(processed_study.Init(&study, false));
Alexei Svitkinec808b532018-03-15 23:53:38432 // Since there's multiple different features, |associated_features| should now
433 // contain them all.
434 EXPECT_THAT(processed_study.associated_features(),
435 ElementsAre(kFeature1Name, kFeature2Name));
asvitkine64e9e112016-03-17 17:32:00436
437 exp1->clear_feature_association();
438 exp1->mutable_feature_association()->add_enable_feature(kFeature1Name);
439 exp2->mutable_feature_association()->add_enable_feature(kFeature1Name);
440 exp3->mutable_feature_association()->add_disable_feature(kFeature1Name);
441 EXPECT_TRUE(processed_study.Init(&study, false));
Alexei Svitkine5bbc9152018-02-27 16:02:30442 EXPECT_THAT(processed_study.associated_features(),
443 ElementsAre(kFeature1Name));
asvitkine64e9e112016-03-17 17:32:00444
Alexei Svitkine5bbc9152018-02-27 16:02:30445 // Setting a different feature name on exp2 should cause |associated_features|
Alexei Svitkinec808b532018-03-15 23:53:38446 // to contain both feature names.
asvitkine64e9e112016-03-17 17:32:00447 exp2->mutable_feature_association()->set_enable_feature(0, kFeature2Name);
448 EXPECT_TRUE(processed_study.Init(&study, false));
Alexei Svitkinec808b532018-03-15 23:53:38449 EXPECT_THAT(processed_study.associated_features(),
450 ElementsAre(kFeature1Name, kFeature2Name));
451
452 // Setting a different activation type should result in empty
453 // |associated_features|.
Steven Holte93e681c2019-01-30 00:33:01454 study.set_activation_type(Study_ActivationType_ACTIVATE_ON_STARTUP);
Alexei Svitkinec808b532018-03-15 23:53:38455 EXPECT_TRUE(processed_study.Init(&study, false));
Alexei Svitkine5bbc9152018-02-27 16:02:30456 EXPECT_THAT(processed_study.associated_features(), IsEmpty());
asvitkine64e9e112016-03-17 17:32:00457}
458
asvitkine30ac09132015-02-20 19:50:10459TEST_F(VariationsSeedProcessorTest, ProcessedStudyAllAssignmentsToOneGroup) {
460 Study study;
461 study.set_default_experiment_name("def");
462 AddExperiment("def", 100, &study);
463
464 ProcessedStudy processed_study;
465 EXPECT_TRUE(processed_study.Init(&study, false));
466 EXPECT_TRUE(processed_study.all_assignments_to_one_group());
467
468 AddExperiment("abc", 0, &study);
469 AddExperiment("flag", 0, &study)->set_forcing_flag(kForcingFlag1);
470 EXPECT_TRUE(processed_study.Init(&study, false));
471 EXPECT_TRUE(processed_study.all_assignments_to_one_group());
472
473 AddExperiment("xyz", 1, &study);
474 EXPECT_TRUE(processed_study.Init(&study, false));
475 EXPECT_FALSE(processed_study.all_assignments_to_one_group());
476
477 // Try with default group and first group being at 0.
478 Study study2;
479 study2.set_default_experiment_name("def");
480 AddExperiment("def", 0, &study2);
481 AddExperiment("xyz", 34, &study2);
482 EXPECT_TRUE(processed_study.Init(&study2, false));
483 EXPECT_TRUE(processed_study.all_assignments_to_one_group());
484 AddExperiment("abc", 12, &study2);
485 EXPECT_TRUE(processed_study.Init(&study2, false));
486 EXPECT_FALSE(processed_study.all_assignments_to_one_group());
487}
488
[email protected]f2fc7cf42013-11-07 21:53:03489TEST_F(VariationsSeedProcessorTest, VariationParams) {
asvitkine71411062016-05-17 20:09:44490 base::FieldTrialList field_trial_list(nullptr);
[email protected]1871a1702013-07-26 09:37:43491
492 Study study;
493 study.set_name("Study1");
494 study.set_default_experiment_name("B");
495
[email protected]5bd00ce2013-08-05 23:46:12496 Study_Experiment* experiment1 = AddExperiment("A", 1, &study);
[email protected]1871a1702013-07-26 09:37:43497 Study_Experiment_Param* param = experiment1->add_param();
498 param->set_name("x");
499 param->set_value("y");
500
[email protected]5bd00ce2013-08-05 23:46:12501 Study_Experiment* experiment2 = AddExperiment("B", 0, &study);
[email protected]1871a1702013-07-26 09:37:43502
asvitkine71411062016-05-17 20:09:44503 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]1871a1702013-07-26 09:37:43504 EXPECT_EQ("y", GetVariationParamValue("Study1", "x"));
505
506 study.set_name("Study2");
507 experiment1->set_probability_weight(0);
508 experiment2->set_probability_weight(1);
asvitkine71411062016-05-17 20:09:44509 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]1871a1702013-07-26 09:37:43510 EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x"));
511}
512
[email protected]f2fc7cf42013-11-07 21:53:03513TEST_F(VariationsSeedProcessorTest, VariationParamsWithForcingFlag) {
514 Study study = CreateStudyWithFlagGroups(100, 0, 0);
515 ASSERT_EQ(kForcingFlag1, study.experiment(1).forcing_flag());
516 Study_Experiment_Param* param = study.mutable_experiment(1)->add_param();
517 param->set_name("x");
518 param->set_value("y");
519
avi1772c1a2014-12-22 22:42:33520 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
asvitkine71411062016-05-17 20:09:44521 base::FieldTrialList field_trial_list(nullptr);
522 EXPECT_TRUE(CreateTrialFromStudy(study));
[email protected]f2fc7cf42013-11-07 21:53:03523 EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study.name()));
524 EXPECT_EQ("y", GetVariationParamValue(study.name(), "x"));
525}
526
527TEST_F(VariationsSeedProcessorTest, StartsActive) {
asvitkine71411062016-05-17 20:09:44528 base::FieldTrialList field_trial_list(nullptr);
[email protected]f0b95fa2013-10-10 21:35:11529
530 VariationsSeed seed;
531 Study* study1 = seed.add_study();
532 study1->set_name("A");
533 study1->set_default_experiment_name("Default");
534 AddExperiment("AA", 100, study1);
535 AddExperiment("Default", 0, study1);
536
537 Study* study2 = seed.add_study();
538 study2->set_name("B");
539 study2->set_default_experiment_name("Default");
540 AddExperiment("BB", 100, study2);
541 AddExperiment("Default", 0, study2);
Steven Holte93e681c2019-01-30 00:33:01542 study2->set_activation_type(Study_ActivationType_ACTIVATE_ON_STARTUP);
[email protected]f0b95fa2013-10-10 21:35:11543
544 Study* study3 = seed.add_study();
545 study3->set_name("C");
546 study3->set_default_experiment_name("Default");
547 AddExperiment("CC", 100, study3);
548 AddExperiment("Default", 0, study3);
Steven Holte93e681c2019-01-30 00:33:01549 study3->set_activation_type(Study_ActivationType_ACTIVATE_ON_QUERY);
[email protected]f0b95fa2013-10-10 21:35:11550
isherman52a81bd2017-06-07 23:30:06551 ClientFilterableState client_state;
552 client_state.locale = "en-CA";
553 client_state.reference_date = base::Time::Now();
554 client_state.version = base::Version("20.0.0.0");
555 client_state.channel = Study::STABLE;
556 client_state.form_factor = Study::DESKTOP;
557 client_state.platform = Study::PLATFORM_ANDROID;
558
[email protected]f0b95fa2013-10-10 21:35:11559 VariationsSeedProcessor seed_processor;
isherman52a81bd2017-06-07 23:30:06560 seed_processor.CreateTrialsFromSeed(seed, client_state,
561 override_callback_.callback(), nullptr,
562 &feature_list_);
[email protected]f0b95fa2013-10-10 21:35:11563
Steven Holte93e681c2019-01-30 00:33:01564 // Non-specified and ACTIVATE_ON_QUERY should not start active, but
565 // ACTIVATE_ON_STARTUP should.
asvitkine11f882b42015-09-24 16:57:39566 EXPECT_FALSE(base::FieldTrialList::IsTrialActive("A"));
567 EXPECT_TRUE(base::FieldTrialList::IsTrialActive("B"));
568 EXPECT_FALSE(base::FieldTrialList::IsTrialActive("C"));
[email protected]f0b95fa2013-10-10 21:35:11569
570 EXPECT_EQ("AA", base::FieldTrialList::FindFullName("A"));
571 EXPECT_EQ("BB", base::FieldTrialList::FindFullName("B"));
572 EXPECT_EQ("CC", base::FieldTrialList::FindFullName("C"));
573
574 // Now, all studies should be active.
asvitkine11f882b42015-09-24 16:57:39575 EXPECT_TRUE(base::FieldTrialList::IsTrialActive("A"));
576 EXPECT_TRUE(base::FieldTrialList::IsTrialActive("B"));
577 EXPECT_TRUE(base::FieldTrialList::IsTrialActive("C"));
[email protected]f0b95fa2013-10-10 21:35:11578}
579
[email protected]85868c4a2014-02-07 18:23:05580TEST_F(VariationsSeedProcessorTest, StartsActiveWithFlag) {
avi1772c1a2014-12-22 22:42:33581 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
[email protected]85868c4a2014-02-07 18:23:05582
asvitkine71411062016-05-17 20:09:44583 base::FieldTrialList field_trial_list(nullptr);
[email protected]85868c4a2014-02-07 18:23:05584
585 Study study = CreateStudyWithFlagGroups(100, 0, 0);
Steven Holte93e681c2019-01-30 00:33:01586 study.set_activation_type(Study_ActivationType_ACTIVATE_ON_STARTUP);
[email protected]85868c4a2014-02-07 18:23:05587
asvitkine71411062016-05-17 20:09:44588 EXPECT_TRUE(CreateTrialFromStudy(study));
asvitkine11f882b42015-09-24 16:57:39589 EXPECT_TRUE(base::FieldTrialList::IsTrialActive(kFlagStudyName));
[email protected]85868c4a2014-02-07 18:23:05590
591 EXPECT_EQ(kFlagGroup1Name,
592 base::FieldTrialList::FindFullName(kFlagStudyName));
593}
594
asvitkine2955e422014-09-03 18:15:40595TEST_F(VariationsSeedProcessorTest, ForcingFlagAlreadyForced) {
596 Study study = CreateStudyWithFlagGroups(100, 0, 0);
597 ASSERT_EQ(kNonFlagGroupName, study.experiment(0).name());
598 Study_Experiment_Param* param = study.mutable_experiment(0)->add_param();
599 param->set_name("x");
600 param->set_value("y");
601 study.mutable_experiment(0)->set_google_web_experiment_id(kExperimentId);
602
asvitkine71411062016-05-17 20:09:44603 base::FieldTrialList field_trial_list(nullptr);
asvitkine2955e422014-09-03 18:15:40604 base::FieldTrialList::CreateFieldTrial(kFlagStudyName, kNonFlagGroupName);
605
avi1772c1a2014-12-22 22:42:33606 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
asvitkine71411062016-05-17 20:09:44607 EXPECT_TRUE(CreateTrialFromStudy(study));
asvitkine2955e422014-09-03 18:15:40608 // The previously forced experiment should still hold.
609 EXPECT_EQ(kNonFlagGroupName,
610 base::FieldTrialList::FindFullName(study.name()));
611
612 // Check that params and experiment ids correspond.
613 EXPECT_EQ("y", GetVariationParamValue(study.name(), "x"));
614 VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName,
615 kNonFlagGroupName);
616 EXPECT_EQ(kExperimentId, id);
617}
618
asvitkine8423d172015-09-28 23:23:44619TEST_F(VariationsSeedProcessorTest, FeatureEnabledOrDisableByTrial) {
620 struct base::Feature kFeatureOffByDefault {
621 "kOff", base::FEATURE_DISABLED_BY_DEFAULT
622 };
623 struct base::Feature kFeatureOnByDefault {
624 "kOn", base::FEATURE_ENABLED_BY_DEFAULT
625 };
626 struct base::Feature kUnrelatedFeature {
627 "kUnrelated", base::FEATURE_DISABLED_BY_DEFAULT
628 };
629
630 struct {
631 const char* enable_feature;
632 const char* disable_feature;
633 bool expected_feature_off_state;
634 bool expected_feature_on_state;
635 } test_cases[] = {
636 {nullptr, nullptr, false, true},
637 {kFeatureOnByDefault.name, nullptr, false, true},
638 {kFeatureOffByDefault.name, nullptr, true, true},
639 {nullptr, kFeatureOnByDefault.name, false, false},
640 {nullptr, kFeatureOffByDefault.name, false, true},
641 };
642
Byoungkown1bb50222018-09-11 01:14:41643 for (size_t i = 0; i < base::size(test_cases); i++) {
asvitkine8423d172015-09-28 23:23:44644 const auto& test_case = test_cases[i];
645 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i));
646
asvitkine71411062016-05-17 20:09:44647 base::FieldTrialList field_trial_list(nullptr);
dcheng3f767dc32016-04-25 22:54:22648 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
asvitkine8423d172015-09-28 23:23:44649
650 Study study;
651 study.set_name("Study1");
652 study.set_default_experiment_name("B");
653 AddExperiment("B", 0, &study);
654
655 Study_Experiment* experiment = AddExperiment("A", 1, &study);
656 Study_Experiment_FeatureAssociation* association =
657 experiment->mutable_feature_association();
658 if (test_case.enable_feature)
659 association->add_enable_feature(test_case.enable_feature);
660 else if (test_case.disable_feature)
661 association->add_disable_feature(test_case.disable_feature);
662
asvitkine71411062016-05-17 20:09:44663 EXPECT_TRUE(CreateTrialFromStudyWithFeatureList(study, feature_list.get()));
asvitkine9499b8d2016-08-09 05:37:07664 base::test::ScopedFeatureList scoped_feature_list;
665 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
asvitkine8423d172015-09-28 23:23:44666
667 // |kUnrelatedFeature| should not be affected.
668 EXPECT_FALSE(base::FeatureList::IsEnabled(kUnrelatedFeature));
669
670 // Before the associated feature is queried, the trial shouldn't be active.
671 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study.name()));
672
673 EXPECT_EQ(test_case.expected_feature_off_state,
674 base::FeatureList::IsEnabled(kFeatureOffByDefault));
675 EXPECT_EQ(test_case.expected_feature_on_state,
676 base::FeatureList::IsEnabled(kFeatureOnByDefault));
677
678 // The field trial should get activated if it had a feature association.
679 const bool expected_field_trial_active =
680 test_case.enable_feature || test_case.disable_feature;
681 EXPECT_EQ(expected_field_trial_active,
682 base::FieldTrialList::IsTrialActive(study.name()));
683 }
684}
685
686TEST_F(VariationsSeedProcessorTest, FeatureAssociationAndForcing) {
687 struct base::Feature kFeatureOffByDefault {
688 "kFeatureOffByDefault", base::FEATURE_DISABLED_BY_DEFAULT
689 };
690 struct base::Feature kFeatureOnByDefault {
691 "kFeatureOnByDefault", base::FEATURE_ENABLED_BY_DEFAULT
692 };
693
694 enum OneHundredPercentGroup {
695 DEFAULT_GROUP,
696 ENABLE_GROUP,
697 DISABLE_GROUP,
698 };
699
700 const char kDefaultGroup[] = "Default";
701 const char kEnabledGroup[] = "Enabled";
702 const char kDisabledGroup[] = "Disabled";
703 const char kForcedOnGroup[] = "ForcedOn";
704 const char kForcedOffGroup[] = "ForcedOff";
705
706 struct {
707 const base::Feature& feature;
708 const char* enable_features_command_line;
709 const char* disable_features_command_line;
710 OneHundredPercentGroup one_hundred_percent_group;
711
712 const char* expected_group;
713 bool expected_feature_state;
714 bool expected_trial_activated;
715 } test_cases[] = {
716 // Check what happens without and command-line forcing flags - that the
717 // |one_hundred_percent_group| gets correctly selected and does the right
718 // thing w.r.t. to affecting the feature / activating the trial.
asvitkine64e9e112016-03-17 17:32:00719 {kFeatureOffByDefault, "", "", DEFAULT_GROUP, kDefaultGroup, false, true},
asvitkine8423d172015-09-28 23:23:44720 {kFeatureOffByDefault, "", "", ENABLE_GROUP, kEnabledGroup, true, true},
721 {kFeatureOffByDefault, "", "", DISABLE_GROUP, kDisabledGroup, false,
722 true},
723
724 // Do the same as above, but for kFeatureOnByDefault feature.
asvitkine64e9e112016-03-17 17:32:00725 {kFeatureOnByDefault, "", "", DEFAULT_GROUP, kDefaultGroup, true, true},
asvitkine8423d172015-09-28 23:23:44726 {kFeatureOnByDefault, "", "", ENABLE_GROUP, kEnabledGroup, true, true},
727 {kFeatureOnByDefault, "", "", DISABLE_GROUP, kDisabledGroup, false, true},
728
729 // Test forcing each feature on and off through the command-line and that
730 // the correct associated experiment gets chosen.
731 {kFeatureOffByDefault, kFeatureOffByDefault.name, "", DEFAULT_GROUP,
732 kForcedOnGroup, true, true},
733 {kFeatureOffByDefault, "", kFeatureOffByDefault.name, DEFAULT_GROUP,
734 kForcedOffGroup, false, true},
735 {kFeatureOnByDefault, kFeatureOnByDefault.name, "", DEFAULT_GROUP,
736 kForcedOnGroup, true, true},
737 {kFeatureOnByDefault, "", kFeatureOnByDefault.name, DEFAULT_GROUP,
738 kForcedOffGroup, false, true},
739
740 // Check that even if a feature should be enabled or disabled based on the
741 // the experiment probability weights, the forcing flag association still
742 // takes precedence. This is 4 cases as above, but with different values
743 // for |one_hundred_percent_group|.
744 {kFeatureOffByDefault, kFeatureOffByDefault.name, "", ENABLE_GROUP,
745 kForcedOnGroup, true, true},
746 {kFeatureOffByDefault, "", kFeatureOffByDefault.name, ENABLE_GROUP,
747 kForcedOffGroup, false, true},
748 {kFeatureOnByDefault, kFeatureOnByDefault.name, "", ENABLE_GROUP,
749 kForcedOnGroup, true, true},
750 {kFeatureOnByDefault, "", kFeatureOnByDefault.name, ENABLE_GROUP,
751 kForcedOffGroup, false, true},
752 {kFeatureOffByDefault, kFeatureOffByDefault.name, "", DISABLE_GROUP,
753 kForcedOnGroup, true, true},
754 {kFeatureOffByDefault, "", kFeatureOffByDefault.name, DISABLE_GROUP,
755 kForcedOffGroup, false, true},
756 {kFeatureOnByDefault, kFeatureOnByDefault.name, "", DISABLE_GROUP,
757 kForcedOnGroup, true, true},
758 {kFeatureOnByDefault, "", kFeatureOnByDefault.name, DISABLE_GROUP,
759 kForcedOffGroup, false, true},
760 };
761
Byoungkown1bb50222018-09-11 01:14:41762 for (size_t i = 0; i < base::size(test_cases); i++) {
asvitkine8423d172015-09-28 23:23:44763 const auto& test_case = test_cases[i];
764 const int group = test_case.one_hundred_percent_group;
765 SCOPED_TRACE(base::StringPrintf(
766 "Test[%" PRIuS "]: %s [%s] [%s] %d", i, test_case.feature.name,
767 test_case.enable_features_command_line,
768 test_case.disable_features_command_line, static_cast<int>(group)));
769
asvitkine71411062016-05-17 20:09:44770 base::FieldTrialList field_trial_list(nullptr);
dcheng3f767dc32016-04-25 22:54:22771 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
asvitkine8423d172015-09-28 23:23:44772 feature_list->InitializeFromCommandLine(
773 test_case.enable_features_command_line,
774 test_case.disable_features_command_line);
775
776 Study study;
777 study.set_name("Study1");
asvitkine71411062016-05-17 20:09:44778 study.set_default_experiment_name(kDefaultGroup);
asvitkine8423d172015-09-28 23:23:44779 AddExperiment(kDefaultGroup, group == DEFAULT_GROUP ? 1 : 0, &study);
780
781 Study_Experiment* feature_enable =
782 AddExperiment(kEnabledGroup, group == ENABLE_GROUP ? 1 : 0, &study);
783 feature_enable->mutable_feature_association()->add_enable_feature(
784 test_case.feature.name);
785
786 Study_Experiment* feature_disable =
787 AddExperiment(kDisabledGroup, group == DISABLE_GROUP ? 1 : 0, &study);
788 feature_disable->mutable_feature_association()->add_disable_feature(
789 test_case.feature.name);
790
791 AddExperiment(kForcedOnGroup, 0, &study)
792 ->mutable_feature_association()
793 ->set_forcing_feature_on(test_case.feature.name);
794 AddExperiment(kForcedOffGroup, 0, &study)
795 ->mutable_feature_association()
796 ->set_forcing_feature_off(test_case.feature.name);
797
asvitkine71411062016-05-17 20:09:44798 EXPECT_TRUE(CreateTrialFromStudyWithFeatureList(study, feature_list.get()));
asvitkine9499b8d2016-08-09 05:37:07799 base::test::ScopedFeatureList scoped_feature_list;
800 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
asvitkine8423d172015-09-28 23:23:44801
802 // Trial should not be activated initially, but later might get activated
803 // depending on the expected values.
804 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study.name()));
805 EXPECT_EQ(test_case.expected_feature_state,
806 base::FeatureList::IsEnabled(test_case.feature));
807 EXPECT_EQ(test_case.expected_trial_activated,
808 base::FieldTrialList::IsTrialActive(study.name()));
809 }
810}
811
asvitkine71411062016-05-17 20:09:44812TEST_F(VariationsSeedProcessorTest, FeaturesInExpiredStudies) {
813 struct base::Feature kDisabledFeature {
814 "kDisabledFeature", base::FEATURE_DISABLED_BY_DEFAULT
815 };
816 struct base::Feature kEnabledFeature {
817 "kEnabledFeature", base::FEATURE_ENABLED_BY_DEFAULT
818 };
819 const base::Time now = base::Time::Now();
820 const base::Time year_ago = now - base::TimeDelta::FromDays(365);
821 const base::Time year_later = now + base::TimeDelta::FromDays(365);
822
823 struct {
824 const base::Feature& feature;
825 bool study_force_feature_state;
826 base::Time expiry_date;
827 bool expected_feature_enabled;
828 } test_cases[] = {
829 {kDisabledFeature, true, year_ago, false},
830 {kDisabledFeature, true, year_later, true},
831 {kEnabledFeature, false, year_ago, true},
832 {kEnabledFeature, false, year_later, false},
833 };
834
Byoungkown1bb50222018-09-11 01:14:41835 for (size_t i = 0; i < base::size(test_cases); i++) {
asvitkine71411062016-05-17 20:09:44836 const auto& test_case = test_cases[i];
837 SCOPED_TRACE(
838 base::StringPrintf("Test[%" PRIuS "]: %s", i, test_case.feature.name));
839
840 base::FieldTrialList field_trial_list(nullptr);
asvitkine71411062016-05-17 20:09:44841 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
842 feature_list->InitializeFromCommandLine(std::string(), std::string());
843
844 // Expired study with a 100% feature group and a default group that has no
845 // feature association.
846 Study study;
847 study.set_name("Study1");
848 study.set_default_experiment_name("Default");
849
850 study.set_expiry_date(TimeToProtoTime(test_case.expiry_date));
851
852 AddExperiment("Default", 0, &study);
853 Study_Experiment* feature_experiment = AddExperiment("Feature", 1, &study);
854 if (test_case.study_force_feature_state) {
855 feature_experiment->mutable_feature_association()->add_enable_feature(
856 test_case.feature.name);
857 } else {
858 feature_experiment->mutable_feature_association()->add_disable_feature(
859 test_case.feature.name);
860 }
861
862 EXPECT_TRUE(CreateTrialFromStudyWithFeatureList(study, feature_list.get()));
asvitkine9499b8d2016-08-09 05:37:07863 base::test::ScopedFeatureList scoped_feature_list;
864 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
asvitkine71411062016-05-17 20:09:44865
jwd5ff54102017-01-06 18:09:34866 // The feature should not be enabled, because the study is expired.
asvitkine71411062016-05-17 20:09:44867 EXPECT_EQ(test_case.expected_feature_enabled,
868 base::FeatureList::IsEnabled(test_case.feature));
869 }
870}
871
jwd5ff54102017-01-06 18:09:34872TEST_F(VariationsSeedProcessorTest, NoDefaultExperiment) {
873 base::FieldTrialList field_trial_list(nullptr);
874
875 Study study;
876 study.set_name("Study1");
877
878 AddExperiment("A", 1, &study);
879
880 EXPECT_TRUE(CreateTrialFromStudy(study));
881
882 base::FieldTrial* trial = base::FieldTrialList::Find("Study1");
883 trial->Disable();
884
885 EXPECT_EQ(ProcessedStudy::kGenericDefaultExperimentName,
886 base::FieldTrialList::FindFullName("Study1"));
887}
888
asvitkine3cac35f2017-06-03 02:02:27889TEST_F(VariationsSeedProcessorTest, ExistingFieldTrial_ExpiredByConfig) {
890 static struct base::Feature kFeature {
891 "FeatureName", base::FEATURE_ENABLED_BY_DEFAULT
892 };
893 base::FieldTrialList field_trial_list(nullptr);
894
895 // In this case, an existing forced trial exists with a different default
896 // group than the study config, which is expired. This tests that we don't
897 // crash in such a case.
898 auto* trial = base::FieldTrialList::FactoryGetFieldTrial(
899 "Study1", 100, "ExistingDefault", base::FieldTrialList::kNoExpirationYear,
900 1, 1, base::FieldTrial::SESSION_RANDOMIZED, nullptr);
901 trial->AppendGroup("A", 100);
902 trial->SetForced();
903
904 Study study;
905 study.set_name("Study1");
906 const base::Time year_ago =
907 base::Time::Now() - base::TimeDelta::FromDays(365);
908 study.set_expiry_date(TimeToProtoTime(year_ago));
909 auto* exp1 = AddExperiment("A", 1, &study);
910 exp1->mutable_feature_association()->add_enable_feature(kFeature.name);
911 AddExperiment("Default", 1, &study);
912 study.set_default_experiment_name("Default");
913
914 EXPECT_TRUE(CreateTrialFromStudy(study));
915
916 // The expected effect is that processing the server config will expire
917 // the existing trial.
918 EXPECT_EQ("ExistingDefault", trial->group_name());
919}
920
921TEST_F(VariationsSeedProcessorTest, ExpiredStudy_NoDefaultGroup) {
922 static struct base::Feature kFeature {
923 "FeatureName", base::FEATURE_ENABLED_BY_DEFAULT
924 };
925 base::FieldTrialList field_trial_list(nullptr);
926
927 // Although it's not expected for the server to provide a study with an expiry
928 // date set, but not default experiment, this tests that we don't crash if
929 // that happens.
930 Study study;
931 study.set_name("Study1");
932 const base::Time year_ago =
933 base::Time::Now() - base::TimeDelta::FromDays(365);
934 study.set_expiry_date(TimeToProtoTime(year_ago));
935 auto* exp1 = AddExperiment("A", 1, &study);
936 exp1->mutable_feature_association()->add_enable_feature(kFeature.name);
937
Alexei Svitkine59e4b212018-05-04 18:05:44938 EXPECT_FALSE(study.has_default_experiment_name());
asvitkine3cac35f2017-06-03 02:02:27939 EXPECT_TRUE(CreateTrialFromStudy(study));
940 EXPECT_EQ("VariationsDefaultExperiment",
941 base::FieldTrialList::FindFullName("Study1"));
942}
943
jwd67c08f752016-05-18 21:04:59944TEST_F(VariationsSeedProcessorTest, LowEntropyStudyTest) {
945 const std::string kTrial1Name = "A";
946 const std::string kTrial2Name = "B";
947 const std::string kGroup1Name = "AA";
948 const std::string kDefaultName = "Default";
949
950 VariationsSeed seed;
951 Study* study1 = seed.add_study();
952 study1->set_name(kTrial1Name);
ishermanb1917aabe2017-06-13 23:35:14953 study1->set_consistency(Study::PERMANENT);
jwd67c08f752016-05-18 21:04:59954 study1->set_default_experiment_name(kDefaultName);
955 AddExperiment(kGroup1Name, 50, study1);
956 AddExperiment(kDefaultName, 50, study1);
957 Study* study2 = seed.add_study();
958 study2->set_name(kTrial2Name);
ishermanb1917aabe2017-06-13 23:35:14959 study2->set_consistency(Study::PERMANENT);
jwd67c08f752016-05-18 21:04:59960 study2->set_default_experiment_name(kDefaultName);
961 AddExperiment(kGroup1Name, 50, study2);
962 AddExperiment(kDefaultName, 50, study2);
963 study2->mutable_experiment(0)->set_google_web_experiment_id(kExperimentId);
964
965 // An entorpy value of 0.1 will cause the AA group to be chosen, since AA is
966 // the only non-default group, and has a probability percent above 0.1.
robliao79393ffb2016-09-21 18:45:29967 base::FieldTrialList field_trial_list(
Jinho Bangc3bcb5c2018-01-15 16:13:00968 std::make_unique<base::MockEntropyProvider>(0.1));
jwd67c08f752016-05-18 21:04:59969
970 // Use a stack instance, since nothing takes ownership of this provider.
971 // This entropy value will cause the default group to be chosen since it's a
972 // 50/50 trial.
973 base::MockEntropyProvider mock_low_entropy_provider(0.9);
974
975 EXPECT_TRUE(CreateTrialFromStudyWithEntropyOverride(
976 *study1, &mock_low_entropy_provider));
977 EXPECT_TRUE(CreateTrialFromStudyWithEntropyOverride(
978 *study2, &mock_low_entropy_provider));
979
980 // Since no experiment in study1 sends experiment IDs, it will use the high
981 // entropy provider, which selects the non-default group.
982 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrial1Name));
983
984 // Since an experiment in study2 has google_web_experiment_id set, it will use
985 // the low entropy provider, which selects the default group.
986 EXPECT_EQ(kDefaultName, base::FieldTrialList::FindFullName(kTrial2Name));
987}
988
[email protected]59b6f672014-07-26 18:35:47989} // namespace variations