Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(88)

Side by Side Diff: components/variations/variations_seed_processor_unittest.cc

Issue 1306653004: Expand FeatureList to support FieldTrial association. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address Rob's comments. Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/variations/variations_seed_processor.h" 5 #include "components/variations/variations_seed_processor.h"
6 6
7 #include <map> 7 #include <map>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/command_line.h" 11 #include "base/command_line.h"
12 #include "base/feature_list.h"
12 #include "base/strings/string_split.h" 13 #include "base/strings/string_split.h"
13 #include "base/strings/utf_string_conversions.h" 14 #include "base/strings/utf_string_conversions.h"
14 #include "components/variations/processed_study.h" 15 #include "components/variations/processed_study.h"
15 #include "components/variations/variations_associated_data.h" 16 #include "components/variations/variations_associated_data.h"
16 #include "testing/gtest/include/gtest/gtest.h" 17 #include "testing/gtest/include/gtest/gtest.h"
17 18
18 namespace variations { 19 namespace variations {
19 20
20 namespace { 21 namespace {
21 22
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 } 113 }
113 114
114 ~VariationsSeedProcessorTest() override { 115 ~VariationsSeedProcessorTest() override {
115 // Ensure that the maps are cleared between tests, since they are stored as 116 // Ensure that the maps are cleared between tests, since they are stored as
116 // process singletons. 117 // process singletons.
117 testing::ClearAllVariationIDs(); 118 testing::ClearAllVariationIDs();
118 testing::ClearAllVariationParams(); 119 testing::ClearAllVariationParams();
119 } 120 }
120 121
121 bool CreateTrialFromStudy(const Study* study) { 122 bool CreateTrialFromStudy(const Study* study) {
123 return CreateTrialFromStudyWithFeatureList(study, &feature_list_);
124 }
125
126 bool CreateTrialFromStudyWithFeatureList(const Study* study,
127 base::FeatureList* feature_list) {
122 ProcessedStudy processed_study; 128 ProcessedStudy processed_study;
123 if (processed_study.Init(study, false)) { 129 if (processed_study.Init(study, false)) {
124 VariationsSeedProcessor().CreateTrialFromStudy( 130 VariationsSeedProcessor().CreateTrialFromStudy(
125 processed_study, override_callback_.callback()); 131 processed_study, override_callback_.callback(), feature_list);
126 return true; 132 return true;
127 } 133 }
128 return false; 134 return false;
129 } 135 }
130 136
131 protected: 137 protected:
138 base::FeatureList feature_list_;
132 TestOverrideStringCallback override_callback_; 139 TestOverrideStringCallback override_callback_;
133 140
134 private: 141 private:
135 DISALLOW_COPY_AND_ASSIGN(VariationsSeedProcessorTest); 142 DISALLOW_COPY_AND_ASSIGN(VariationsSeedProcessorTest);
136 }; 143 };
137 144
138 TEST_F(VariationsSeedProcessorTest, AllowForceGroupAndVariationId) { 145 TEST_F(VariationsSeedProcessorTest, AllowForceGroupAndVariationId) {
139 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1); 146 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
140 147
141 base::FieldTrialList field_trial_list(NULL); 148 base::FieldTrialList field_trial_list(NULL);
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 ASSERT_EQ(seed.study(0).name(), seed.study(1).name()); 226 ASSERT_EQ(seed.study(0).name(), seed.study(1).name());
220 227
221 const base::Time year_ago = 228 const base::Time year_ago =
222 base::Time::Now() - base::TimeDelta::FromDays(365); 229 base::Time::Now() - base::TimeDelta::FromDays(365);
223 230
224 const base::Version version("20.0.0.0"); 231 const base::Version version("20.0.0.0");
225 232
226 // Check that adding [expired, non-expired] activates the non-expired one. 233 // Check that adding [expired, non-expired] activates the non-expired one.
227 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName)); 234 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
228 { 235 {
236 base::FeatureList feature_list;
229 base::FieldTrialList field_trial_list(NULL); 237 base::FieldTrialList field_trial_list(NULL);
230 study1->set_expiry_date(TimeToProtoTime(year_ago)); 238 study1->set_expiry_date(TimeToProtoTime(year_ago));
231 seed_processor.CreateTrialsFromSeed( 239 seed_processor.CreateTrialsFromSeed(
232 seed, "en-CA", base::Time::Now(), version, Study_Channel_STABLE, 240 seed, "en-CA", base::Time::Now(), version, Study_Channel_STABLE,
233 Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback()); 241 Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback(),
242 &feature_list);
234 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName)); 243 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
235 } 244 }
236 245
237 // Check that adding [non-expired, expired] activates the non-expired one. 246 // Check that adding [non-expired, expired] activates the non-expired one.
238 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName)); 247 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
239 { 248 {
249 base::FeatureList feature_list;
240 base::FieldTrialList field_trial_list(NULL); 250 base::FieldTrialList field_trial_list(NULL);
241 study1->clear_expiry_date(); 251 study1->clear_expiry_date();
242 study2->set_expiry_date(TimeToProtoTime(year_ago)); 252 study2->set_expiry_date(TimeToProtoTime(year_ago));
243 seed_processor.CreateTrialsFromSeed( 253 seed_processor.CreateTrialsFromSeed(
244 seed, "en-CA", base::Time::Now(), version, Study_Channel_STABLE, 254 seed, "en-CA", base::Time::Now(), version, Study_Channel_STABLE,
245 Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback()); 255 Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback(),
256 &feature_list);
246 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName)); 257 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
247 } 258 }
248 } 259 }
249 260
250 TEST_F(VariationsSeedProcessorTest, OverrideUIStrings) { 261 TEST_F(VariationsSeedProcessorTest, OverrideUIStrings) {
251 base::FieldTrialList field_trial_list(NULL); 262 base::FieldTrialList field_trial_list(NULL);
252 263
253 Study study; 264 Study study;
254 study.set_name("Study1"); 265 study.set_name("Study1");
255 study.set_default_experiment_name("B"); 266 study.set_default_experiment_name("B");
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 study3->set_name("C"); 451 study3->set_name("C");
441 study3->set_default_experiment_name("Default"); 452 study3->set_default_experiment_name("Default");
442 AddExperiment("CC", 100, study3); 453 AddExperiment("CC", 100, study3);
443 AddExperiment("Default", 0, study3); 454 AddExperiment("Default", 0, study3);
444 study3->set_activation_type(Study_ActivationType_ACTIVATION_EXPLICIT); 455 study3->set_activation_type(Study_ActivationType_ACTIVATION_EXPLICIT);
445 456
446 VariationsSeedProcessor seed_processor; 457 VariationsSeedProcessor seed_processor;
447 seed_processor.CreateTrialsFromSeed( 458 seed_processor.CreateTrialsFromSeed(
448 seed, "en-CA", base::Time::Now(), base::Version("20.0.0.0"), 459 seed, "en-CA", base::Time::Now(), base::Version("20.0.0.0"),
449 Study_Channel_STABLE, Study_FormFactor_DESKTOP, "", "", "", 460 Study_Channel_STABLE, Study_FormFactor_DESKTOP, "", "", "",
450 override_callback_.callback()); 461 override_callback_.callback(), &feature_list_);
451 462
452 // Non-specified and ACTIVATION_EXPLICIT should not start active, but 463 // Non-specified and ACTIVATION_EXPLICIT should not start active, but
453 // ACTIVATION_AUTO should. 464 // ACTIVATION_AUTO should.
454 EXPECT_FALSE(IsFieldTrialActive("A")); 465 EXPECT_FALSE(IsFieldTrialActive("A"));
455 EXPECT_TRUE(IsFieldTrialActive("B")); 466 EXPECT_TRUE(IsFieldTrialActive("B"));
456 EXPECT_FALSE(IsFieldTrialActive("C")); 467 EXPECT_FALSE(IsFieldTrialActive("C"));
457 468
458 EXPECT_EQ("AA", base::FieldTrialList::FindFullName("A")); 469 EXPECT_EQ("AA", base::FieldTrialList::FindFullName("A"));
459 EXPECT_EQ("BB", base::FieldTrialList::FindFullName("B")); 470 EXPECT_EQ("BB", base::FieldTrialList::FindFullName("B"));
460 EXPECT_EQ("CC", base::FieldTrialList::FindFullName("C")); 471 EXPECT_EQ("CC", base::FieldTrialList::FindFullName("C"));
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
497 EXPECT_EQ(kNonFlagGroupName, 508 EXPECT_EQ(kNonFlagGroupName,
498 base::FieldTrialList::FindFullName(study.name())); 509 base::FieldTrialList::FindFullName(study.name()));
499 510
500 // Check that params and experiment ids correspond. 511 // Check that params and experiment ids correspond.
501 EXPECT_EQ("y", GetVariationParamValue(study.name(), "x")); 512 EXPECT_EQ("y", GetVariationParamValue(study.name(), "x"));
502 VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName, 513 VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName,
503 kNonFlagGroupName); 514 kNonFlagGroupName);
504 EXPECT_EQ(kExperimentId, id); 515 EXPECT_EQ(kExperimentId, id);
505 } 516 }
506 517
518 TEST_F(VariationsSeedProcessorTest, FeatureEnabledOrDisableByTrial) {
519 struct base::Feature kFeatureOffByDefault {
520 "kOff", base::FEATURE_DISABLED_BY_DEFAULT
521 };
522 struct base::Feature kFeatureOnByDefault {
523 "kOn", base::FEATURE_ENABLED_BY_DEFAULT
524 };
525 struct base::Feature kUnrelatedFeature {
526 "kUnrelated", base::FEATURE_DISABLED_BY_DEFAULT
527 };
528
529 struct {
530 const char* enable_feature;
531 const char* disable_feature;
532 bool expected_feature_off_state;
533 bool expected_feature_on_state;
534 } test_cases[] = {
535 {nullptr, nullptr, false, true},
536 {kFeatureOnByDefault.name, nullptr, false, true},
537 {kFeatureOffByDefault.name, nullptr, true, true},
538 {nullptr, kFeatureOnByDefault.name, false, false},
539 {nullptr, kFeatureOffByDefault.name, false, true},
540 };
541
542 for (size_t i = 0; i < arraysize(test_cases); i++) {
543 const auto& test_case = test_cases[i];
544
545 base::FieldTrialList field_trial_list(NULL);
546 base::FeatureList::ClearInstanceForTesting();
Ilya Sherman 2015/09/16 00:50:44 Should this also be called at the end of each test
Alexei Svitkine (slow) 2015/09/22 21:19:59 Good point. Put in the dtor of the harness.
547 scoped_ptr<base::FeatureList> feature_list(new base::FeatureList);
548
549 Study study;
550 study.set_name("Study1");
551 study.set_default_experiment_name("B");
552 AddExperiment("B", 0, &study);
553
554 Study_Experiment* experiment = AddExperiment("A", 1, &study);
555 Study_Experiment_FeatureAssociation* association =
556 experiment->mutable_feature_association();
557 if (test_case.enable_feature)
558 association->add_enable_feature(test_case.enable_feature);
559 else if (test_case.disable_feature)
560 association->add_disable_feature(test_case.disable_feature);
561
562 EXPECT_TRUE(
563 CreateTrialFromStudyWithFeatureList(&study, feature_list.get()));
564 base::FeatureList::SetInstance(feature_list.Pass());
565
566 // |kUnrelatedFeature| should not be affected.
567 EXPECT_FALSE(base::FeatureList::IsEnabled(kUnrelatedFeature));
568
569 // Before the associated feature is queried, the trial shouldn't be active.
570 EXPECT_FALSE(IsFieldTrialActive(study.name()));
571
572 EXPECT_EQ(test_case.expected_feature_off_state,
573 base::FeatureList::IsEnabled(kFeatureOffByDefault));
574 EXPECT_EQ(test_case.expected_feature_on_state,
575 base::FeatureList::IsEnabled(kFeatureOnByDefault));
576
577 // The field trial should get activated if it had a feature association.
578 const bool expected_field_trial_active =
579 test_case.enable_feature || test_case.disable_feature;
580 EXPECT_EQ(expected_field_trial_active, IsFieldTrialActive(study.name()));
581 }
582 }
583
584 TEST_F(VariationsSeedProcessorTest, FeatureForcingTrialReportingGroup) {
585 struct base::Feature kFeatureOffByDefault {
586 "kOff", base::FEATURE_DISABLED_BY_DEFAULT
587 };
588 struct base::Feature kFeatureOnByDefault {
589 "kOn", base::FEATURE_ENABLED_BY_DEFAULT
590 };
591
592 struct {
593 const base::Feature& feature;
594 bool set_forcing_feature_on;
595 bool set_forcing_feature_off;
596 const char* enable_features_command_line;
597 const char* disable_features_command_line;
598 bool expected_feature_state;
599 bool expected_trial_activated;
600 } test_cases[] = {
601 {kFeatureOffByDefault, false, false, "", "", false, false},
602 {kFeatureOffByDefault, true, false, kFeatureOffByDefault.name, "", true,
603 true},
604 {kFeatureOffByDefault, false, true, kFeatureOffByDefault.name, "", true,
605 false},
606 {kFeatureOffByDefault, false, true, "", kFeatureOffByDefault.name, false,
607 true},
608 {kFeatureOffByDefault, true, false, "", kFeatureOffByDefault.name, false,
609 false},
610 {kFeatureOnByDefault, false, false, "", "", true, false},
611 {kFeatureOnByDefault, true, false, kFeatureOnByDefault.name, "", true,
612 true},
613 {kFeatureOnByDefault, false, true, kFeatureOnByDefault.name, "", true,
614 false},
615 {kFeatureOnByDefault, false, true, "", kFeatureOnByDefault.name, false,
616 true},
617 {kFeatureOnByDefault, true, false, "", kFeatureOnByDefault.name, false,
618 false},
619 };
Ilya Sherman 2015/09/16 00:50:44 This test is pretty dense, making it hard to read.
Alexei Svitkine (slow) 2015/09/22 21:19:59 Let me think about. I started it as individual tes
Alexei Svitkine (slow) 2015/09/23 22:01:27 Thinking about this more, I agree that this test i
Ilya Sherman 2015/09/24 01:11:32 The comments help a bunch -- thanks! I think my m
Alexei Svitkine (slow) 2015/09/24 20:49:21 I agree that complicated table-driven tests are ha
620
621 for (size_t i = 0; i < arraysize(test_cases); i++) {
622 const auto& test_case = test_cases[i];
623
624 base::FieldTrialList field_trial_list(NULL);
625 base::FeatureList::ClearInstanceForTesting();
626 scoped_ptr<base::FeatureList> feature_list(new base::FeatureList);
627 feature_list->InitializeFromCommandLine(
628 test_case.enable_features_command_line,
629 test_case.disable_features_command_line);
630
631 Study study;
632 study.set_name("Study1");
633 study.set_default_experiment_name("B");
634 Study_Experiment* experiment_with_probability =
635 AddExperiment("B", 1, &study);
636
637 Study_Experiment* experiment = AddExperiment("A", 0, &study);
638 Study_Experiment_FeatureAssociation* association =
639 experiment->mutable_feature_association();
640 if (test_case.set_forcing_feature_on)
641 association->set_forcing_feature_on(test_case.feature.name);
642 else if (test_case.set_forcing_feature_off)
643 association->set_forcing_feature_off(test_case.feature.name);
644
645 EXPECT_TRUE(
646 CreateTrialFromStudyWithFeatureList(&study, feature_list.get()));
647 base::FeatureList::SetInstance(feature_list.Pass());
648
649 // Trial should not be activated initially.
650 EXPECT_FALSE(IsFieldTrialActive(study.name()));
651 EXPECT_EQ(test_case.expected_feature_state,
652 base::FeatureList::IsEnabled(test_case.feature));
653 EXPECT_EQ(test_case.expected_trial_activated,
654 IsFieldTrialActive(study.name()));
655 if (test_case.expected_trial_activated) {
656 EXPECT_EQ(experiment->name(),
657 base::FieldTrialList::FindFullName(study.name()));
658 } else {
659 EXPECT_EQ(experiment_with_probability->name(),
660 base::FieldTrialList::FindFullName(study.name()));
661 }
662 }
663 }
Ilya Sherman 2015/09/16 00:50:44 It would be nice to add tests for what happens in
Alexei Svitkine (slow) 2015/09/16 20:54:08 This is meant to be enforced server-side. I guess
Ilya Sherman 2015/09/17 01:02:46 I guess for things that are strictly enforced serv
664
507 } // namespace variations 665 } // namespace variations
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698