variations_seed_processor_unittest.cc 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  1. // 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. #include "components/variations/variations_seed_processor.h"
  5. #include <stddef.h>
  6. #include <stdint.h>
  7. #include <map>
  8. #include <memory>
  9. #include <utility>
  10. #include <vector>
  11. #include "base/bind.h"
  12. #include "base/callback_helpers.h"
  13. #include "base/command_line.h"
  14. #include "base/feature_list.h"
  15. #include "base/format_macros.h"
  16. #include "base/strings/string_split.h"
  17. #include "base/strings/stringprintf.h"
  18. #include "base/strings/utf_string_conversions.h"
  19. #include "base/test/metrics/histogram_tester.h"
  20. #include "base/test/mock_entropy_provider.h"
  21. #include "base/test/scoped_feature_list.h"
  22. #include "base/time/time.h"
  23. #include "components/variations/client_filterable_state.h"
  24. #include "components/variations/processed_study.h"
  25. #include "components/variations/proto/study.pb.h"
  26. #include "components/variations/study_filtering.h"
  27. #include "components/variations/variations_associated_data.h"
  28. #include "testing/gmock/include/gmock/gmock.h"
  29. #include "testing/gtest/include/gtest/gtest.h"
  30. using testing::ElementsAre;
  31. using testing::IsEmpty;
  32. namespace variations {
  33. namespace {
  34. // Converts |time| to Study proto format.
  35. int64_t TimeToProtoTime(const base::Time& time) {
  36. return (time - base::Time::UnixEpoch()).InSeconds();
  37. }
  38. // Constants for testing associating command line flags with trial groups.
  39. const char kFlagStudyName[] = "flag_test_trial";
  40. const char kFlagGroup1Name[] = "flag_group1";
  41. const char kFlagGroup2Name[] = "flag_group2";
  42. const char kNonFlagGroupName[] = "non_flag_group";
  43. const char kOtherGroupName[] = "other_group";
  44. const char kForcingFlag1[] = "flag_test1";
  45. const char kForcingFlag2[] = "flag_test2";
  46. const VariationID kExperimentId = 123;
  47. // Adds an experiment to |study| with the specified |name| and |probability|.
  48. Study::Experiment* AddExperiment(const std::string& name,
  49. int probability,
  50. Study* study) {
  51. Study::Experiment* experiment = study->add_experiment();
  52. experiment->set_name(name);
  53. experiment->set_probability_weight(probability);
  54. return experiment;
  55. }
  56. // Populates |study| with test data used for testing associating command line
  57. // flags with trials groups. The study will contain three groups, a default
  58. // group that isn't associated with a flag, and two other groups, both
  59. // associated with different flags.
  60. Study CreateStudyWithFlagGroups(int default_group_probability,
  61. int flag_group1_probability,
  62. int flag_group2_probability) {
  63. DCHECK_GE(default_group_probability, 0);
  64. DCHECK_GE(flag_group1_probability, 0);
  65. DCHECK_GE(flag_group2_probability, 0);
  66. Study study;
  67. study.set_name(kFlagStudyName);
  68. study.set_default_experiment_name(kNonFlagGroupName);
  69. AddExperiment(kNonFlagGroupName, default_group_probability, &study);
  70. AddExperiment(kFlagGroup1Name, flag_group1_probability, &study)
  71. ->set_forcing_flag(kForcingFlag1);
  72. AddExperiment(kFlagGroup2Name, flag_group2_probability, &study)
  73. ->set_forcing_flag(kForcingFlag2);
  74. return study;
  75. }
  76. class TestOverrideStringCallback {
  77. public:
  78. typedef std::map<uint32_t, std::u16string> OverrideMap;
  79. TestOverrideStringCallback()
  80. : callback_(base::BindRepeating(&TestOverrideStringCallback::Override,
  81. base::Unretained(this))) {}
  82. TestOverrideStringCallback(const TestOverrideStringCallback&) = delete;
  83. TestOverrideStringCallback& operator=(const TestOverrideStringCallback&) =
  84. delete;
  85. virtual ~TestOverrideStringCallback() {}
  86. const VariationsSeedProcessor::UIStringOverrideCallback& callback() const {
  87. return callback_;
  88. }
  89. const OverrideMap& overrides() const { return overrides_; }
  90. private:
  91. void Override(uint32_t hash, const std::u16string& string) {
  92. overrides_[hash] = string;
  93. }
  94. VariationsSeedProcessor::UIStringOverrideCallback callback_;
  95. OverrideMap overrides_;
  96. };
  97. } // namespace
  98. class VariationsSeedProcessorTest : public ::testing::Test {
  99. public:
  100. VariationsSeedProcessorTest() {
  101. }
  102. VariationsSeedProcessorTest(const VariationsSeedProcessorTest&) = delete;
  103. VariationsSeedProcessorTest& operator=(const VariationsSeedProcessorTest&) =
  104. delete;
  105. ~VariationsSeedProcessorTest() override {
  106. // Ensure that the maps are cleared between tests, since they are stored as
  107. // process singletons.
  108. testing::ClearAllVariationIDs();
  109. testing::ClearAllVariationParams();
  110. }
  111. bool CreateTrialFromStudy(const Study& study) {
  112. base::MockEntropyProvider mock_low_entropy_provider(0.9);
  113. return CreateTrialFromStudyWithFeatureListAndEntropyOverride(
  114. study, mock_low_entropy_provider, base::FeatureList::GetInstance());
  115. }
  116. bool CreateTrialFromStudyWithEntropyOverride(
  117. const Study& study,
  118. const base::FieldTrial::EntropyProvider& override_entropy_provider) {
  119. return CreateTrialFromStudyWithFeatureListAndEntropyOverride(
  120. study, override_entropy_provider, base::FeatureList::GetInstance());
  121. }
  122. bool CreateTrialFromStudyWithFeatureList(const Study& study,
  123. base::FeatureList* feature_list) {
  124. base::MockEntropyProvider mock_low_entropy_provider(0.9);
  125. return CreateTrialFromStudyWithFeatureListAndEntropyOverride(
  126. study, mock_low_entropy_provider, feature_list);
  127. }
  128. bool CreateTrialFromStudyWithFeatureListAndEntropyOverride(
  129. const Study& study,
  130. const base::FieldTrial::EntropyProvider& override_entropy_provider,
  131. base::FeatureList* feature_list) {
  132. ProcessedStudy processed_study;
  133. const bool is_expired = internal::IsStudyExpired(study, base::Time::Now());
  134. if (processed_study.Init(&study, is_expired)) {
  135. VariationsSeedProcessor().CreateTrialFromStudy(
  136. processed_study, override_callback_.callback(),
  137. &override_entropy_provider, feature_list);
  138. return true;
  139. }
  140. return false;
  141. }
  142. void CreateTrialsFromSeed(const VariationsSeed& seed,
  143. double low_entropy = 0.9) {
  144. ClientFilterableState client_state(base::BindOnce([] { return false; }));
  145. client_state.locale = "en-CA";
  146. client_state.reference_date = base::Time::Now();
  147. client_state.version = base::Version("20.0.0.0");
  148. client_state.channel = Study::STABLE;
  149. client_state.form_factor = Study::PHONE;
  150. client_state.platform = Study::PLATFORM_ANDROID;
  151. base::FeatureList feature_list;
  152. base::MockEntropyProvider mock_low_entropy_provider(low_entropy);
  153. VariationsSeedProcessor seed_processor;
  154. seed_processor.CreateTrialsFromSeed(
  155. seed, client_state, override_callback_.callback(),
  156. &mock_low_entropy_provider, &feature_list);
  157. }
  158. protected:
  159. TestOverrideStringCallback override_callback_;
  160. };
  161. TEST_F(VariationsSeedProcessorTest, EmitStudyCountMetric) {
  162. struct StudyCountMetricTestParams {
  163. VariationsSeed seed;
  164. int expected_study_count;
  165. };
  166. VariationsSeed zero_study_seed;
  167. VariationsSeed one_study_seed;
  168. Study* study = one_study_seed.add_study();
  169. study->set_name("MyStudy");
  170. AddExperiment("Enabled", 1, study);
  171. std::vector<StudyCountMetricTestParams> test_cases = {
  172. {.seed = zero_study_seed, .expected_study_count = 0},
  173. {.seed = one_study_seed, .expected_study_count = 1}};
  174. for (const StudyCountMetricTestParams& test_case : test_cases) {
  175. base::HistogramTester histogram_tester;
  176. CreateTrialsFromSeed(test_case.seed);
  177. histogram_tester.ExpectUniqueSample("Variations.AppliedSeed.StudyCount",
  178. test_case.expected_study_count, 1);
  179. }
  180. }
  181. TEST_F(VariationsSeedProcessorTest, AllowForceGroupAndVariationId) {
  182. base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
  183. Study study = CreateStudyWithFlagGroups(100, 0, 0);
  184. study.mutable_experiment(1)->set_google_web_experiment_id(kExperimentId);
  185. EXPECT_TRUE(CreateTrialFromStudy(study));
  186. EXPECT_EQ(kFlagGroup1Name,
  187. base::FieldTrialList::FindFullName(kFlagStudyName));
  188. VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
  189. kFlagStudyName, kFlagGroup1Name);
  190. EXPECT_EQ(kExperimentId, id);
  191. }
  192. TEST_F(VariationsSeedProcessorTest, AllowForceGroupAndVariationId_FirstParty) {
  193. base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
  194. Study study = CreateStudyWithFlagGroups(100, 0, 0);
  195. Study::Experiment* experiment1 = study.mutable_experiment(1);
  196. experiment1->set_google_web_experiment_id(kExperimentId);
  197. experiment1->set_google_web_visibility(Study::FIRST_PARTY);
  198. EXPECT_TRUE(CreateTrialFromStudy(study));
  199. EXPECT_EQ(kFlagGroup1Name,
  200. base::FieldTrialList::FindFullName(kFlagStudyName));
  201. VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_FIRST_PARTY,
  202. kFlagStudyName, kFlagGroup1Name);
  203. EXPECT_EQ(kExperimentId, id);
  204. }
  205. // Test that the group for kForcingFlag1 is forced.
  206. TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag1) {
  207. base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
  208. Study study = CreateStudyWithFlagGroups(100, 0, 0);
  209. EXPECT_TRUE(CreateTrialFromStudy(study));
  210. EXPECT_EQ(kFlagGroup1Name,
  211. base::FieldTrialList::FindFullName(kFlagStudyName));
  212. }
  213. // Test that the group for kForcingFlag2 is forced.
  214. TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag2) {
  215. base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
  216. Study study = CreateStudyWithFlagGroups(100, 0, 0);
  217. EXPECT_TRUE(CreateTrialFromStudy(study));
  218. EXPECT_EQ(kFlagGroup2Name,
  219. base::FieldTrialList::FindFullName(kFlagStudyName));
  220. }
  221. TEST_F(VariationsSeedProcessorTest, ForceGroup_ChooseFirstGroupWithFlag) {
  222. // Add the flag to the command line arguments so the flag group is forced.
  223. base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
  224. base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
  225. Study study = CreateStudyWithFlagGroups(100, 0, 0);
  226. EXPECT_TRUE(CreateTrialFromStudy(study));
  227. EXPECT_EQ(kFlagGroup1Name,
  228. base::FieldTrialList::FindFullName(kFlagStudyName));
  229. }
  230. TEST_F(VariationsSeedProcessorTest, ForceGroup_DontChooseGroupWithFlag) {
  231. // The two flag groups are given high probability, which would normally make
  232. // them very likely to be chosen. They won't be chosen since flag groups are
  233. // never chosen when their flag isn't present.
  234. Study study = CreateStudyWithFlagGroups(1, 999, 999);
  235. EXPECT_TRUE(CreateTrialFromStudy(study));
  236. EXPECT_EQ(kNonFlagGroupName,
  237. base::FieldTrialList::FindFullName(kFlagStudyName));
  238. }
  239. TEST_F(VariationsSeedProcessorTest, CreateTrialForRegisteredGroup) {
  240. base::FieldTrialList::CreateFieldTrial(kFlagStudyName, kOtherGroupName);
  241. // Create an arbitrary study that does not have group named |kOtherGroupName|.
  242. Study study = CreateStudyWithFlagGroups(100, 0, 0);
  243. // Creating the trial should not crash.
  244. EXPECT_TRUE(CreateTrialFromStudy(study));
  245. // And the previous group should still be selected.
  246. EXPECT_EQ(kOtherGroupName,
  247. base::FieldTrialList::FindFullName(kFlagStudyName));
  248. }
  249. TEST_F(VariationsSeedProcessorTest,
  250. NonExpiredStudyPrioritizedOverExpiredStudy) {
  251. VariationsSeedProcessor seed_processor;
  252. const std::string kTrialName = "A";
  253. const std::string kGroup1Name = "Group1";
  254. VariationsSeed seed;
  255. Study* study1 = seed.add_study();
  256. study1->set_name(kTrialName);
  257. study1->set_default_experiment_name("Default");
  258. AddExperiment(kGroup1Name, 100, study1);
  259. AddExperiment("Default", 0, study1);
  260. Study* study2 = seed.add_study();
  261. *study2 = *study1;
  262. ASSERT_EQ(seed.study(0).name(), seed.study(1).name());
  263. const base::Time year_ago = base::Time::Now() - base::Days(365);
  264. ClientFilterableState client_state(base::BindOnce([] { return false; }));
  265. client_state.locale = "en-CA";
  266. client_state.reference_date = base::Time::Now();
  267. client_state.version = base::Version("20.0.0.0");
  268. client_state.channel = Study::STABLE;
  269. client_state.form_factor = Study::DESKTOP;
  270. client_state.platform = Study::PLATFORM_ANDROID;
  271. // Check that adding [expired, non-expired] activates the non-expired one.
  272. ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
  273. {
  274. base::test::ScopedFeatureList scoped_feature_list;
  275. scoped_feature_list.Init();
  276. base::FeatureList feature_list;
  277. study1->set_expiry_date(TimeToProtoTime(year_ago));
  278. base::MockEntropyProvider mock_low_entropy_provider(0.9);
  279. seed_processor.CreateTrialsFromSeed(
  280. seed, client_state, override_callback_.callback(),
  281. &mock_low_entropy_provider, &feature_list);
  282. EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
  283. }
  284. // Check that adding [non-expired, expired] activates the non-expired one.
  285. ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
  286. {
  287. base::test::ScopedFeatureList scoped_feature_list;
  288. scoped_feature_list.Init();
  289. base::FeatureList feature_list;
  290. study1->clear_expiry_date();
  291. study2->set_expiry_date(TimeToProtoTime(year_ago));
  292. base::MockEntropyProvider mock_low_entropy_provider(0.9);
  293. seed_processor.CreateTrialsFromSeed(
  294. seed, client_state, override_callback_.callback(),
  295. &mock_low_entropy_provider, &feature_list);
  296. EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
  297. }
  298. }
  299. TEST_F(VariationsSeedProcessorTest, OverrideUIStrings) {
  300. Study study;
  301. study.set_name("Study1");
  302. study.set_default_experiment_name("B");
  303. study.set_activation_type(Study::ACTIVATE_ON_STARTUP);
  304. Study::Experiment* experiment1 = AddExperiment("A", 0, &study);
  305. Study::Experiment::OverrideUIString* override =
  306. experiment1->add_override_ui_string();
  307. override->set_name_hash(1234);
  308. override->set_value("test");
  309. Study::Experiment* experiment2 = AddExperiment("B", 1, &study);
  310. EXPECT_TRUE(CreateTrialFromStudy(study));
  311. const TestOverrideStringCallback::OverrideMap& overrides =
  312. override_callback_.overrides();
  313. EXPECT_TRUE(overrides.empty());
  314. study.set_name("Study2");
  315. experiment1->set_probability_weight(1);
  316. experiment2->set_probability_weight(0);
  317. EXPECT_TRUE(CreateTrialFromStudy(study));
  318. EXPECT_EQ(1u, overrides.size());
  319. auto it = overrides.find(1234);
  320. EXPECT_EQ(u"test", it->second);
  321. }
  322. TEST_F(VariationsSeedProcessorTest, OverrideUIStringsWithForcingFlag) {
  323. Study study = CreateStudyWithFlagGroups(100, 0, 0);
  324. ASSERT_EQ(kForcingFlag1, study.experiment(1).forcing_flag());
  325. study.set_activation_type(Study::ACTIVATE_ON_STARTUP);
  326. Study::Experiment::OverrideUIString* override =
  327. study.mutable_experiment(1)->add_override_ui_string();
  328. override->set_name_hash(1234);
  329. override->set_value("test");
  330. base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
  331. EXPECT_TRUE(CreateTrialFromStudy(study));
  332. EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study.name()));
  333. const TestOverrideStringCallback::OverrideMap& overrides =
  334. override_callback_.overrides();
  335. EXPECT_EQ(1u, overrides.size());
  336. auto it = overrides.find(1234);
  337. EXPECT_EQ(u"test", it->second);
  338. }
  339. TEST_F(VariationsSeedProcessorTest, VariationParams) {
  340. Study study;
  341. study.set_name("Study1");
  342. study.set_default_experiment_name("B");
  343. Study::Experiment* experiment1 = AddExperiment("A", 1, &study);
  344. Study::Experiment::Param* param = experiment1->add_param();
  345. param->set_name("x");
  346. param->set_value("y");
  347. Study::Experiment* experiment2 = AddExperiment("B", 0, &study);
  348. EXPECT_TRUE(CreateTrialFromStudy(study));
  349. EXPECT_EQ("y", GetVariationParamValue("Study1", "x"));
  350. study.set_name("Study2");
  351. experiment1->set_probability_weight(0);
  352. experiment2->set_probability_weight(1);
  353. EXPECT_TRUE(CreateTrialFromStudy(study));
  354. EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x"));
  355. }
  356. TEST_F(VariationsSeedProcessorTest, VariationParamsWithForcingFlag) {
  357. Study study = CreateStudyWithFlagGroups(100, 0, 0);
  358. ASSERT_EQ(kForcingFlag1, study.experiment(1).forcing_flag());
  359. Study::Experiment::Param* param = study.mutable_experiment(1)->add_param();
  360. param->set_name("x");
  361. param->set_value("y");
  362. base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
  363. EXPECT_TRUE(CreateTrialFromStudy(study));
  364. EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study.name()));
  365. EXPECT_EQ("y", GetVariationParamValue(study.name(), "x"));
  366. }
  367. TEST_F(VariationsSeedProcessorTest, StartsActive) {
  368. VariationsSeed seed;
  369. Study* study1 = seed.add_study();
  370. study1->set_name("A");
  371. study1->set_default_experiment_name("Default");
  372. AddExperiment("AA", 100, study1);
  373. AddExperiment("Default", 0, study1);
  374. Study* study2 = seed.add_study();
  375. study2->set_name("B");
  376. study2->set_default_experiment_name("Default");
  377. AddExperiment("BB", 100, study2);
  378. AddExperiment("Default", 0, study2);
  379. study2->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  380. Study* study3 = seed.add_study();
  381. study3->set_name("C");
  382. study3->set_default_experiment_name("Default");
  383. AddExperiment("CC", 100, study3);
  384. AddExperiment("Default", 0, study3);
  385. study3->set_activation_type(Study::ACTIVATE_ON_QUERY);
  386. ClientFilterableState client_state(base::BindOnce([] { return false; }));
  387. client_state.locale = "en-CA";
  388. client_state.reference_date = base::Time::Now();
  389. client_state.version = base::Version("20.0.0.0");
  390. client_state.channel = Study::STABLE;
  391. client_state.form_factor = Study::DESKTOP;
  392. client_state.platform = Study::PLATFORM_ANDROID;
  393. VariationsSeedProcessor seed_processor;
  394. base::MockEntropyProvider mock_low_entropy_provider(0.9);
  395. seed_processor.CreateTrialsFromSeed(
  396. seed, client_state, override_callback_.callback(),
  397. &mock_low_entropy_provider, base::FeatureList::GetInstance());
  398. // Non-specified and ACTIVATE_ON_QUERY should not start active, but
  399. // ACTIVATE_ON_STARTUP should.
  400. EXPECT_FALSE(base::FieldTrialList::IsTrialActive("A"));
  401. EXPECT_TRUE(base::FieldTrialList::IsTrialActive("B"));
  402. EXPECT_FALSE(base::FieldTrialList::IsTrialActive("C"));
  403. EXPECT_EQ("AA", base::FieldTrialList::FindFullName("A"));
  404. EXPECT_EQ("BB", base::FieldTrialList::FindFullName("B"));
  405. EXPECT_EQ("CC", base::FieldTrialList::FindFullName("C"));
  406. // Now, all studies should be active.
  407. EXPECT_TRUE(base::FieldTrialList::IsTrialActive("A"));
  408. EXPECT_TRUE(base::FieldTrialList::IsTrialActive("B"));
  409. EXPECT_TRUE(base::FieldTrialList::IsTrialActive("C"));
  410. }
  411. TEST_F(VariationsSeedProcessorTest, StartsActiveWithFlag) {
  412. base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
  413. Study study = CreateStudyWithFlagGroups(100, 0, 0);
  414. study.set_activation_type(Study::ACTIVATE_ON_STARTUP);
  415. EXPECT_TRUE(CreateTrialFromStudy(study));
  416. EXPECT_TRUE(base::FieldTrialList::IsTrialActive(kFlagStudyName));
  417. EXPECT_EQ(kFlagGroup1Name,
  418. base::FieldTrialList::FindFullName(kFlagStudyName));
  419. }
  420. TEST_F(VariationsSeedProcessorTest, ForcingFlagAlreadyForced) {
  421. Study study = CreateStudyWithFlagGroups(100, 0, 0);
  422. ASSERT_EQ(kNonFlagGroupName, study.experiment(0).name());
  423. Study::Experiment::Param* param = study.mutable_experiment(0)->add_param();
  424. param->set_name("x");
  425. param->set_value("y");
  426. study.mutable_experiment(0)->set_google_web_experiment_id(kExperimentId);
  427. base::FieldTrialList::CreateFieldTrial(kFlagStudyName, kNonFlagGroupName);
  428. base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
  429. EXPECT_TRUE(CreateTrialFromStudy(study));
  430. // The previously forced experiment should still hold.
  431. EXPECT_EQ(kNonFlagGroupName,
  432. base::FieldTrialList::FindFullName(study.name()));
  433. // Check that params and experiment ids correspond.
  434. EXPECT_EQ("y", GetVariationParamValue(study.name(), "x"));
  435. VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
  436. kFlagStudyName, kNonFlagGroupName);
  437. EXPECT_EQ(kExperimentId, id);
  438. }
  439. TEST_F(VariationsSeedProcessorTest, FeatureEnabledOrDisableByTrial) {
  440. struct base::Feature kFeatureOffByDefault {
  441. "kOff", base::FEATURE_DISABLED_BY_DEFAULT
  442. };
  443. struct base::Feature kFeatureOnByDefault {
  444. "kOn", base::FEATURE_ENABLED_BY_DEFAULT
  445. };
  446. struct base::Feature kUnrelatedFeature {
  447. "kUnrelated", base::FEATURE_DISABLED_BY_DEFAULT
  448. };
  449. struct {
  450. const char* enable_feature;
  451. const char* disable_feature;
  452. bool expected_feature_off_state;
  453. bool expected_feature_on_state;
  454. } test_cases[] = {
  455. {nullptr, nullptr, false, true},
  456. {kFeatureOnByDefault.name, nullptr, false, true},
  457. {kFeatureOffByDefault.name, nullptr, true, true},
  458. {nullptr, kFeatureOnByDefault.name, false, false},
  459. {nullptr, kFeatureOffByDefault.name, false, true},
  460. };
  461. for (size_t i = 0; i < std::size(test_cases); i++) {
  462. const auto& test_case = test_cases[i];
  463. SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i));
  464. // Needed for base::FeatureList::GetInstance() when creating field trials.
  465. base::test::ScopedFeatureList base_scoped_feature_list;
  466. base_scoped_feature_list.Init();
  467. std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
  468. Study study;
  469. study.set_name("Study1");
  470. study.set_default_experiment_name("B");
  471. AddExperiment("B", 0, &study);
  472. Study::Experiment* experiment = AddExperiment("A", 1, &study);
  473. Study::Experiment::FeatureAssociation* association =
  474. experiment->mutable_feature_association();
  475. if (test_case.enable_feature)
  476. association->add_enable_feature(test_case.enable_feature);
  477. else if (test_case.disable_feature)
  478. association->add_disable_feature(test_case.disable_feature);
  479. EXPECT_TRUE(CreateTrialFromStudyWithFeatureList(study, feature_list.get()));
  480. base::test::ScopedFeatureList scoped_feature_list;
  481. scoped_feature_list.InitWithFeatureList(std::move(feature_list));
  482. // |kUnrelatedFeature| should not be affected.
  483. EXPECT_FALSE(base::FeatureList::IsEnabled(kUnrelatedFeature));
  484. // Before the associated feature is queried, the trial shouldn't be active.
  485. EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study.name()));
  486. EXPECT_EQ(test_case.expected_feature_off_state,
  487. base::FeatureList::IsEnabled(kFeatureOffByDefault));
  488. EXPECT_EQ(test_case.expected_feature_on_state,
  489. base::FeatureList::IsEnabled(kFeatureOnByDefault));
  490. // The field trial should get activated if it had a feature association.
  491. const bool expected_field_trial_active =
  492. test_case.enable_feature || test_case.disable_feature;
  493. EXPECT_EQ(expected_field_trial_active,
  494. base::FieldTrialList::IsTrialActive(study.name()));
  495. }
  496. }
  497. TEST_F(VariationsSeedProcessorTest, FeatureAssociationAndForcing) {
  498. struct base::Feature kFeatureOffByDefault {
  499. "kFeatureOffByDefault", base::FEATURE_DISABLED_BY_DEFAULT
  500. };
  501. struct base::Feature kFeatureOnByDefault {
  502. "kFeatureOnByDefault", base::FEATURE_ENABLED_BY_DEFAULT
  503. };
  504. enum OneHundredPercentGroup {
  505. DEFAULT_GROUP,
  506. ENABLE_GROUP,
  507. DISABLE_GROUP,
  508. };
  509. const char kDefaultGroup[] = "Default";
  510. const char kEnabledGroup[] = "Enabled";
  511. const char kDisabledGroup[] = "Disabled";
  512. const char kForcedOnGroup[] = "ForcedOn";
  513. const char kForcedOffGroup[] = "ForcedOff";
  514. struct {
  515. const base::Feature& feature;
  516. const char* enable_features_command_line;
  517. const char* disable_features_command_line;
  518. OneHundredPercentGroup one_hundred_percent_group;
  519. const char* expected_group;
  520. bool expected_feature_state;
  521. bool expected_trial_activated;
  522. } test_cases[] = {
  523. // Check what happens without and command-line forcing flags - that the
  524. // |one_hundred_percent_group| gets correctly selected and does the right
  525. // thing w.r.t. to affecting the feature / activating the trial.
  526. {kFeatureOffByDefault, "", "", DEFAULT_GROUP, kDefaultGroup, false, true},
  527. {kFeatureOffByDefault, "", "", ENABLE_GROUP, kEnabledGroup, true, true},
  528. {kFeatureOffByDefault, "", "", DISABLE_GROUP, kDisabledGroup, false,
  529. true},
  530. // Do the same as above, but for kFeatureOnByDefault feature.
  531. {kFeatureOnByDefault, "", "", DEFAULT_GROUP, kDefaultGroup, true, true},
  532. {kFeatureOnByDefault, "", "", ENABLE_GROUP, kEnabledGroup, true, true},
  533. {kFeatureOnByDefault, "", "", DISABLE_GROUP, kDisabledGroup, false, true},
  534. // Test forcing each feature on and off through the command-line and that
  535. // the correct associated experiment gets chosen.
  536. {kFeatureOffByDefault, kFeatureOffByDefault.name, "", DEFAULT_GROUP,
  537. kForcedOnGroup, true, true},
  538. {kFeatureOffByDefault, "", kFeatureOffByDefault.name, DEFAULT_GROUP,
  539. kForcedOffGroup, false, true},
  540. {kFeatureOnByDefault, kFeatureOnByDefault.name, "", DEFAULT_GROUP,
  541. kForcedOnGroup, true, true},
  542. {kFeatureOnByDefault, "", kFeatureOnByDefault.name, DEFAULT_GROUP,
  543. kForcedOffGroup, false, true},
  544. // Check that even if a feature should be enabled or disabled based on the
  545. // the experiment probability weights, the forcing flag association still
  546. // takes precedence. This is 4 cases as above, but with different values
  547. // for |one_hundred_percent_group|.
  548. {kFeatureOffByDefault, kFeatureOffByDefault.name, "", ENABLE_GROUP,
  549. kForcedOnGroup, true, true},
  550. {kFeatureOffByDefault, "", kFeatureOffByDefault.name, ENABLE_GROUP,
  551. kForcedOffGroup, false, true},
  552. {kFeatureOnByDefault, kFeatureOnByDefault.name, "", ENABLE_GROUP,
  553. kForcedOnGroup, true, true},
  554. {kFeatureOnByDefault, "", kFeatureOnByDefault.name, ENABLE_GROUP,
  555. kForcedOffGroup, false, true},
  556. {kFeatureOffByDefault, kFeatureOffByDefault.name, "", DISABLE_GROUP,
  557. kForcedOnGroup, true, true},
  558. {kFeatureOffByDefault, "", kFeatureOffByDefault.name, DISABLE_GROUP,
  559. kForcedOffGroup, false, true},
  560. {kFeatureOnByDefault, kFeatureOnByDefault.name, "", DISABLE_GROUP,
  561. kForcedOnGroup, true, true},
  562. {kFeatureOnByDefault, "", kFeatureOnByDefault.name, DISABLE_GROUP,
  563. kForcedOffGroup, false, true},
  564. };
  565. for (size_t i = 0; i < std::size(test_cases); i++) {
  566. const auto& test_case = test_cases[i];
  567. const int group = test_case.one_hundred_percent_group;
  568. SCOPED_TRACE(base::StringPrintf(
  569. "Test[%" PRIuS "]: %s [%s] [%s] %d", i, test_case.feature.name,
  570. test_case.enable_features_command_line,
  571. test_case.disable_features_command_line, static_cast<int>(group)));
  572. // Needed for base::FeatureList::GetInstance() when creating field trials.
  573. base::test::ScopedFeatureList base_scoped_feature_list;
  574. base_scoped_feature_list.Init();
  575. std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
  576. feature_list->InitializeFromCommandLine(
  577. test_case.enable_features_command_line,
  578. test_case.disable_features_command_line);
  579. Study study;
  580. study.set_name("Study1");
  581. study.set_default_experiment_name(kDefaultGroup);
  582. AddExperiment(kDefaultGroup, group == DEFAULT_GROUP ? 1 : 0, &study);
  583. Study::Experiment* feature_enable =
  584. AddExperiment(kEnabledGroup, group == ENABLE_GROUP ? 1 : 0, &study);
  585. feature_enable->mutable_feature_association()->add_enable_feature(
  586. test_case.feature.name);
  587. Study::Experiment* feature_disable =
  588. AddExperiment(kDisabledGroup, group == DISABLE_GROUP ? 1 : 0, &study);
  589. feature_disable->mutable_feature_association()->add_disable_feature(
  590. test_case.feature.name);
  591. AddExperiment(kForcedOnGroup, 0, &study)
  592. ->mutable_feature_association()
  593. ->set_forcing_feature_on(test_case.feature.name);
  594. AddExperiment(kForcedOffGroup, 0, &study)
  595. ->mutable_feature_association()
  596. ->set_forcing_feature_off(test_case.feature.name);
  597. EXPECT_TRUE(CreateTrialFromStudyWithFeatureList(study, feature_list.get()));
  598. base::test::ScopedFeatureList scoped_feature_list;
  599. scoped_feature_list.InitWithFeatureList(std::move(feature_list));
  600. // Trial should not be activated initially, but later might get activated
  601. // depending on the expected values.
  602. EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study.name()));
  603. EXPECT_EQ(test_case.expected_feature_state,
  604. base::FeatureList::IsEnabled(test_case.feature));
  605. EXPECT_EQ(test_case.expected_trial_activated,
  606. base::FieldTrialList::IsTrialActive(study.name()));
  607. }
  608. }
  609. TEST_F(VariationsSeedProcessorTest, FeaturesInExpiredStudies) {
  610. struct base::Feature kDisabledFeature {
  611. "kDisabledFeature", base::FEATURE_DISABLED_BY_DEFAULT
  612. };
  613. struct base::Feature kEnabledFeature {
  614. "kEnabledFeature", base::FEATURE_ENABLED_BY_DEFAULT
  615. };
  616. const base::Time now = base::Time::Now();
  617. const base::Time year_ago = now - base::Days(365);
  618. const base::Time year_later = now + base::Days(365);
  619. struct {
  620. const base::Feature& feature;
  621. bool study_force_feature_state;
  622. base::Time expiry_date;
  623. bool expected_feature_enabled;
  624. } test_cases[] = {
  625. {kDisabledFeature, true, year_ago, false},
  626. {kDisabledFeature, true, year_later, true},
  627. {kEnabledFeature, false, year_ago, true},
  628. {kEnabledFeature, false, year_later, false},
  629. };
  630. for (size_t i = 0; i < std::size(test_cases); i++) {
  631. const auto& test_case = test_cases[i];
  632. SCOPED_TRACE(
  633. base::StringPrintf("Test[%" PRIuS "]: %s", i, test_case.feature.name));
  634. // Needed for base::FeatureList::GetInstance() when creating field trials.
  635. base::test::ScopedFeatureList base_scoped_feature_list;
  636. base_scoped_feature_list.Init();
  637. std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
  638. feature_list->InitializeFromCommandLine(std::string(), std::string());
  639. // Expired study with a 100% feature group and a default group that has no
  640. // feature association.
  641. Study study;
  642. study.set_name("Study1");
  643. study.set_default_experiment_name("Default");
  644. study.set_expiry_date(TimeToProtoTime(test_case.expiry_date));
  645. AddExperiment("Default", 0, &study);
  646. Study::Experiment* feature_experiment = AddExperiment("Feature", 1, &study);
  647. if (test_case.study_force_feature_state) {
  648. feature_experiment->mutable_feature_association()->add_enable_feature(
  649. test_case.feature.name);
  650. } else {
  651. feature_experiment->mutable_feature_association()->add_disable_feature(
  652. test_case.feature.name);
  653. }
  654. EXPECT_TRUE(CreateTrialFromStudyWithFeatureList(study, feature_list.get()));
  655. base::test::ScopedFeatureList scoped_feature_list;
  656. scoped_feature_list.InitWithFeatureList(std::move(feature_list));
  657. // The feature should not be enabled, because the study is expired.
  658. EXPECT_EQ(test_case.expected_feature_enabled,
  659. base::FeatureList::IsEnabled(test_case.feature));
  660. }
  661. }
  662. TEST_F(VariationsSeedProcessorTest, NoDefaultExperiment) {
  663. Study study;
  664. study.set_name("Study1");
  665. AddExperiment("A", 1, &study);
  666. EXPECT_TRUE(CreateTrialFromStudy(study));
  667. base::FieldTrial* trial = base::FieldTrialList::Find("Study1");
  668. trial->Disable();
  669. EXPECT_EQ(ProcessedStudy::kGenericDefaultExperimentName,
  670. base::FieldTrialList::FindFullName("Study1"));
  671. }
  672. TEST_F(VariationsSeedProcessorTest, ExistingFieldTrial_ExpiredByConfig) {
  673. static struct base::Feature kFeature {
  674. "FeatureName", base::FEATURE_ENABLED_BY_DEFAULT
  675. };
  676. // In this case, an existing forced trial exists with a different default
  677. // group than the study config, which is expired. This tests that we don't
  678. // crash in such a case.
  679. auto* trial = base::FieldTrialList::FactoryGetFieldTrial(
  680. "Study1", 100, "ExistingDefault", base::FieldTrial::SESSION_RANDOMIZED,
  681. nullptr);
  682. trial->AppendGroup("A", 100);
  683. trial->SetForced();
  684. Study study;
  685. study.set_name("Study1");
  686. const base::Time year_ago = base::Time::Now() - base::Days(365);
  687. study.set_expiry_date(TimeToProtoTime(year_ago));
  688. auto* exp1 = AddExperiment("A", 1, &study);
  689. exp1->mutable_feature_association()->add_enable_feature(kFeature.name);
  690. AddExperiment("Default", 1, &study);
  691. study.set_default_experiment_name("Default");
  692. EXPECT_TRUE(CreateTrialFromStudy(study));
  693. // The expected effect is that processing the server config will expire
  694. // the existing trial.
  695. EXPECT_EQ("ExistingDefault", trial->group_name());
  696. }
  697. TEST_F(VariationsSeedProcessorTest, ExpiredStudy_NoDefaultGroup) {
  698. static struct base::Feature kFeature {
  699. "FeatureName", base::FEATURE_ENABLED_BY_DEFAULT
  700. };
  701. // Although it's not expected for the server to provide a study with an expiry
  702. // date set, but not default experiment, this tests that we don't crash if
  703. // that happens.
  704. Study study;
  705. study.set_name("Study1");
  706. const base::Time year_ago = base::Time::Now() - base::Days(365);
  707. study.set_expiry_date(TimeToProtoTime(year_ago));
  708. auto* exp1 = AddExperiment("A", 1, &study);
  709. exp1->mutable_feature_association()->add_enable_feature(kFeature.name);
  710. EXPECT_FALSE(study.has_default_experiment_name());
  711. EXPECT_TRUE(CreateTrialFromStudy(study));
  712. EXPECT_EQ("VariationsDefaultExperiment",
  713. base::FieldTrialList::FindFullName("Study1"));
  714. }
  715. TEST_F(VariationsSeedProcessorTest, LowEntropyStudyTest) {
  716. const std::string kTrial1Name = "A";
  717. const std::string kTrial2Name = "B";
  718. const std::string kGroup1Name = "AA";
  719. const std::string kDefaultName = "Default";
  720. VariationsSeed seed;
  721. Study* study1 = seed.add_study();
  722. study1->set_name(kTrial1Name);
  723. study1->set_consistency(Study::PERMANENT);
  724. study1->set_default_experiment_name(kDefaultName);
  725. AddExperiment(kGroup1Name, 50, study1);
  726. AddExperiment(kDefaultName, 50, study1);
  727. Study* study2 = seed.add_study();
  728. study2->set_name(kTrial2Name);
  729. study2->set_consistency(Study::PERMANENT);
  730. study2->set_default_experiment_name(kDefaultName);
  731. AddExperiment(kGroup1Name, 50, study2);
  732. AddExperiment(kDefaultName, 50, study2);
  733. study2->mutable_experiment(0)->set_google_web_experiment_id(kExperimentId);
  734. // An entropy value of 0.1 will cause the AA group to be chosen, since AA is
  735. // the only non-default group, and has a probability percent above 0.1.
  736. base::test::ScopedFeatureList scoped_feature_list;
  737. scoped_feature_list.InitWithNullFeatureAndFieldTrialLists();
  738. base::FieldTrialList field_trial_list(
  739. std::make_unique<base::MockEntropyProvider>(0.1));
  740. // Use a stack instance, since nothing takes ownership of this provider.
  741. // This entropy value will cause the default group to be chosen since it's a
  742. // 50/50 trial.
  743. base::MockEntropyProvider mock_low_entropy_provider(0.9);
  744. EXPECT_TRUE(CreateTrialFromStudyWithEntropyOverride(
  745. *study1, mock_low_entropy_provider));
  746. EXPECT_TRUE(CreateTrialFromStudyWithEntropyOverride(
  747. *study2, mock_low_entropy_provider));
  748. // Since no experiment in study1 sends experiment IDs, it will use the high
  749. // entropy provider, which selects the non-default group.
  750. EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrial1Name));
  751. // Since an experiment in study2 has google_web_experiment_id set, it will use
  752. // the low entropy provider, which selects the default group.
  753. EXPECT_EQ(kDefaultName, base::FieldTrialList::FindFullName(kTrial2Name));
  754. }
  755. TEST_F(VariationsSeedProcessorTest, StudyWithInvalidLayer) {
  756. VariationsSeed seed;
  757. Study* study = seed.add_study();
  758. study->set_name("Study1");
  759. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  760. LayerMemberReference* layer = study->mutable_layer();
  761. layer->set_layer_id(42);
  762. layer->set_layer_member_id(82);
  763. AddExperiment("A", 1, study);
  764. CreateTrialsFromSeed(seed);
  765. // Since the studies references a layer which doesn't exist, it should
  766. // select the default group.
  767. EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study->name()));
  768. }
  769. TEST_F(VariationsSeedProcessorTest, StudyWithInvalidLayerMember) {
  770. VariationsSeed seed;
  771. Layer* layer = seed.add_layers();
  772. layer->set_id(42);
  773. layer->set_num_slots(1);
  774. Layer::LayerMember* member = layer->add_members();
  775. member->set_id(2);
  776. Layer::LayerMember::SlotRange* slot = member->add_slots();
  777. slot->set_start(0);
  778. slot->set_end(0);
  779. Study* study = seed.add_study();
  780. study->set_name("Study1");
  781. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  782. LayerMemberReference* layer_membership = study->mutable_layer();
  783. layer_membership->set_layer_id(42);
  784. layer_membership->set_layer_member_id(88);
  785. AddExperiment("A", 1, study);
  786. CreateTrialsFromSeed(seed);
  787. // Since the studies references a layer member which doesn't exist, it should
  788. // not be active.
  789. EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study->name()));
  790. }
  791. TEST_F(VariationsSeedProcessorTest, StudyWithLayerSelected) {
  792. VariationsSeed seed;
  793. Layer* layer = seed.add_layers();
  794. layer->set_id(42);
  795. layer->set_num_slots(1);
  796. Layer::LayerMember* member = layer->add_members();
  797. member->set_id(82);
  798. Layer::LayerMember::SlotRange* slot = member->add_slots();
  799. slot->set_start(0);
  800. slot->set_end(0);
  801. Study* study = seed.add_study();
  802. study->set_name("Study1");
  803. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  804. LayerMemberReference* layer_membership = study->mutable_layer();
  805. layer_membership->set_layer_id(42);
  806. layer_membership->set_layer_member_id(82);
  807. AddExperiment("A", 1, study);
  808. CreateTrialsFromSeed(seed);
  809. // The layer only has the single member, which is what should be chosen.
  810. EXPECT_TRUE(base::FieldTrialList::IsTrialActive(study->name()));
  811. }
  812. TEST_F(VariationsSeedProcessorTest, StudyWithLayerMemberWithNoSlots) {
  813. VariationsSeed seed;
  814. Layer* layer = seed.add_layers();
  815. layer->set_id(42);
  816. layer->set_num_slots(10);
  817. Layer::LayerMember* member = layer->add_members();
  818. member->set_id(82);
  819. // Add one SlotRange, with no slots actually defined.
  820. member->add_slots();
  821. Study* study = seed.add_study();
  822. study->set_name("Study1");
  823. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  824. LayerMemberReference* layer_membership = study->mutable_layer();
  825. layer_membership->set_layer_id(42);
  826. layer_membership->set_layer_member_id(82);
  827. AddExperiment("A", 1, study);
  828. CreateTrialsFromSeed(seed);
  829. // The layer member referenced by the study is missing slots, and should
  830. // never be chosen.
  831. EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study->name()));
  832. }
  833. TEST_F(VariationsSeedProcessorTest, StudyWithLayerWithDuplicateSlots) {
  834. VariationsSeed seed;
  835. Layer* layer = seed.add_layers();
  836. layer->set_id(42);
  837. layer->set_num_slots(1);
  838. Layer::LayerMember* member = layer->add_members();
  839. member->set_id(82);
  840. Layer::LayerMember::SlotRange* first_slot = member->add_slots();
  841. first_slot->set_start(0);
  842. first_slot->set_end(0);
  843. // A second overlapping slot.
  844. Layer::LayerMember::SlotRange* second_slot = member->add_slots();
  845. second_slot->set_start(0);
  846. second_slot->set_end(0);
  847. Study* study = seed.add_study();
  848. study->set_name("Study1");
  849. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  850. LayerMemberReference* layer_membership = study->mutable_layer();
  851. layer_membership->set_layer_id(42);
  852. layer_membership->set_layer_member_id(82);
  853. AddExperiment("A", 1, study);
  854. CreateTrialsFromSeed(seed);
  855. // The layer only has the single member, which is what should be chosen.
  856. // Having two duplicate slot ranges within that member should not crash.
  857. EXPECT_TRUE(base::FieldTrialList::IsTrialActive(study->name()));
  858. }
  859. TEST_F(VariationsSeedProcessorTest, StudyWithLayerMemberWithOutOfRangeSlots) {
  860. VariationsSeed seed;
  861. Layer* layer = seed.add_layers();
  862. layer->set_id(42);
  863. layer->set_num_slots(10);
  864. Layer::LayerMember* member = layer->add_members();
  865. member->set_id(82);
  866. Layer::LayerMember::SlotRange* overshooting_slot = member->add_slots();
  867. overshooting_slot->set_start(20);
  868. overshooting_slot->set_end(50);
  869. Study* study = seed.add_study();
  870. study->set_name("Study1");
  871. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  872. LayerMemberReference* layer_membership = study->mutable_layer();
  873. layer_membership->set_layer_id(42);
  874. layer_membership->set_layer_member_id(82);
  875. AddExperiment("A", 1, study);
  876. CreateTrialsFromSeed(seed);
  877. // The layer member referenced by the study is missing slots, and should
  878. // never be chosen.
  879. EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study->name()));
  880. }
  881. TEST_F(VariationsSeedProcessorTest, StudyWithLayerMemberWithReversedSlots) {
  882. VariationsSeed seed;
  883. Layer* layer = seed.add_layers();
  884. layer->set_id(42);
  885. layer->set_num_slots(10);
  886. Layer::LayerMember* member = layer->add_members();
  887. member->set_id(82);
  888. Layer::LayerMember::SlotRange* overshooting_slot = member->add_slots();
  889. overshooting_slot->set_start(8);
  890. overshooting_slot->set_end(2);
  891. Study* study = seed.add_study();
  892. study->set_name("Study1");
  893. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  894. LayerMemberReference* layer_membership = study->mutable_layer();
  895. layer_membership->set_layer_id(42);
  896. layer_membership->set_layer_member_id(82);
  897. AddExperiment("A", 1, study);
  898. CreateTrialsFromSeed(seed);
  899. // The layer member referenced by the study is has its slots in the wrong
  900. // order (end < start) which should cause the slot to never be chosen
  901. // (and not crash).
  902. EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study->name()));
  903. }
  904. TEST_F(VariationsSeedProcessorTest, StudyWithLayerNotSelected) {
  905. VariationsSeed seed;
  906. Layer* layer = seed.add_layers();
  907. layer->set_id(42);
  908. layer->set_num_slots(8000);
  909. // Setting this forces the provided entropy provider to be used when
  910. // calling CreateTrialsFromSeed.
  911. layer->set_entropy_mode(Layer::LOW);
  912. // Member with most slots, but won't be chosen due to the entropy provided.
  913. {
  914. Layer::LayerMember* member = layer->add_members();
  915. member->set_id(0xDEAD);
  916. Layer::LayerMember::SlotRange* slot = member->add_slots();
  917. slot->set_start(0);
  918. slot->set_end(7900);
  919. }
  920. // Member with few slots, but will be chosen.
  921. {
  922. Layer::LayerMember* member = layer->add_members();
  923. member->set_id(0xBEEF);
  924. Layer::LayerMember::SlotRange* slot = member->add_slots();
  925. slot->set_start(7901);
  926. slot->set_end(7999);
  927. }
  928. Study* study = seed.add_study();
  929. study->set_name("Study1");
  930. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  931. LayerMemberReference* layer_membership = study->mutable_layer();
  932. layer_membership->set_layer_id(42);
  933. layer_membership->set_layer_member_id(0xDEAD);
  934. AddExperiment("A", 1, study);
  935. // Entropy 0.99 Should cause slot 7920 to be chosen.
  936. CreateTrialsFromSeed(seed, /*low_entropy=*/0.99);
  937. // The study is a member of the 0xDEAD layer member and should be inactive.
  938. EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study->name()));
  939. }
  940. TEST_F(VariationsSeedProcessorTest, LayerWithDefaultEntropy) {
  941. VariationsSeed seed;
  942. Layer* layer = seed.add_layers();
  943. layer->set_id(42);
  944. layer->set_num_slots(8000);
  945. // Member which should get chosen by the default high entropy source
  946. // (which defaults to half of the num_slots in tests).
  947. {
  948. Layer::LayerMember* member = layer->add_members();
  949. member->set_id(0xDEAD);
  950. Layer::LayerMember::SlotRange* slot = member->add_slots();
  951. slot->set_start(0);
  952. slot->set_end(7900);
  953. }
  954. // Member with few slots,
  955. {
  956. Layer::LayerMember* member = layer->add_members();
  957. member->set_id(0xBEEF);
  958. Layer::LayerMember::SlotRange* slot = member->add_slots();
  959. slot->set_start(7901);
  960. slot->set_end(7999);
  961. }
  962. Study* study = seed.add_study();
  963. study->set_name("Study1");
  964. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  965. LayerMemberReference* layer_membership = study->mutable_layer();
  966. layer_membership->set_layer_id(42);
  967. layer_membership->set_layer_member_id(0xDEAD);
  968. AddExperiment("A", 1, study);
  969. // Since we're *not* setting the entropy_mode to LOW, |low_entropy| should
  970. // be ignored and the default high entropy should be used, which in
  971. // this case is slot 4000 and hence the first layer member is chosen.
  972. CreateTrialsFromSeed(seed, /*low_entropy=*/0.99);
  973. // The study is a member of the 0xDEAD layer member and should be active.
  974. EXPECT_TRUE(base::FieldTrialList::IsTrialActive(study->name()));
  975. }
  976. TEST_F(VariationsSeedProcessorTest, LayerWithNoMembers) {
  977. VariationsSeed seed;
  978. Layer* layer = seed.add_layers();
  979. layer->set_id(1);
  980. layer->set_num_slots(1);
  981. layer->set_salt(0xBEEF);
  982. // Layer should be rejected and not crash.
  983. CreateTrialsFromSeed(seed);
  984. }
  985. TEST_F(VariationsSeedProcessorTest, LayerWithNoSlots) {
  986. VariationsSeed seed;
  987. Layer* layer = seed.add_layers();
  988. layer->set_id(1);
  989. layer->set_salt(0xBEEF);
  990. // Layer should be rejected and not crash.
  991. CreateTrialsFromSeed(seed);
  992. }
  993. TEST_F(VariationsSeedProcessorTest, LayerWithNoID) {
  994. VariationsSeed seed;
  995. Layer* layer = seed.add_layers();
  996. layer->set_salt(0xBEEF);
  997. // Layer should be rejected and not crash.
  998. CreateTrialsFromSeed(seed);
  999. }
  1000. TEST_F(VariationsSeedProcessorTest, EmptyLayer) {
  1001. VariationsSeed seed;
  1002. seed.add_layers();
  1003. // Layer should be rejected and not crash.
  1004. CreateTrialsFromSeed(seed);
  1005. }
  1006. TEST_F(VariationsSeedProcessorTest, LayersWithDuplicateID) {
  1007. VariationsSeed seed;
  1008. {
  1009. Layer* layer = seed.add_layers();
  1010. layer->set_id(1);
  1011. layer->set_salt(0xBEEF);
  1012. layer->set_num_slots(1);
  1013. Layer::LayerMember* member = layer->add_members();
  1014. member->set_id(82);
  1015. Layer::LayerMember::SlotRange* slot = member->add_slots();
  1016. slot->set_start(0);
  1017. slot->set_end(0);
  1018. }
  1019. {
  1020. Layer* layer = seed.add_layers();
  1021. layer->set_id(1);
  1022. layer->set_salt(0xBEEF);
  1023. layer->set_num_slots(1);
  1024. Layer::LayerMember* member = layer->add_members();
  1025. member->set_id(82);
  1026. Layer::LayerMember::SlotRange* slot = member->add_slots();
  1027. slot->set_start(0);
  1028. slot->set_end(0);
  1029. }
  1030. // The duplicate layer should be rejected and not crash.
  1031. CreateTrialsFromSeed(seed);
  1032. }
  1033. TEST_F(VariationsSeedProcessorTest, StudyWithLayerMemberWithoutID) {
  1034. VariationsSeed seed;
  1035. Layer* layer = seed.add_layers();
  1036. layer->set_id(42);
  1037. layer->set_num_slots(1);
  1038. Layer::LayerMember* member = layer->add_members();
  1039. Layer::LayerMember::SlotRange* slot = member->add_slots();
  1040. slot->set_start(0);
  1041. slot->set_end(0);
  1042. Study* study = seed.add_study();
  1043. study->set_name("Study1");
  1044. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  1045. LayerMemberReference* layer_membership = study->mutable_layer();
  1046. layer_membership->set_layer_id(42);
  1047. AddExperiment("A", 1, study);
  1048. CreateTrialsFromSeed(seed);
  1049. // The layer only has the single member but that member has no
  1050. // ID set. The LayerMembership also has no member_id set. The study
  1051. // should then *not* be chosen (i.e. a default initialized ID of 0
  1052. // should not be seen as valid.)
  1053. EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study->name()));
  1054. }
  1055. TEST_F(VariationsSeedProcessorTest, StudyWithLowerEntropyThanLayer) {
  1056. VariationsSeed seed;
  1057. Layer* layer = seed.add_layers();
  1058. layer->set_id(42);
  1059. layer->set_num_slots(1);
  1060. Layer::LayerMember* member = layer->add_members();
  1061. member->set_id(82);
  1062. Layer::LayerMember::SlotRange* slot = member->add_slots();
  1063. slot->set_start(0);
  1064. slot->set_end(0);
  1065. Study* study = seed.add_study();
  1066. study->set_name("Study1");
  1067. study->set_activation_type(Study::ACTIVATE_ON_STARTUP);
  1068. LayerMemberReference* layer_membership = study->mutable_layer();
  1069. layer_membership->set_layer_id(42);
  1070. layer_membership->set_layer_member_id(82);
  1071. AddExperiment("A", 1, study);
  1072. study->mutable_experiment(0)->set_google_web_experiment_id(kExperimentId);
  1073. CreateTrialsFromSeed(seed);
  1074. // Since the study will use the low entropy source and the layer the default
  1075. // one, the study should be rejected.
  1076. EXPECT_FALSE(base::FieldTrialList::IsTrialActive(study->name()));
  1077. }
  1078. } // namespace variations