Chromium Code Reviews| Index: base/test/scoped_feature_list.cc | 
| diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc | 
| index f0f3f4edfba1f1d333ef3188836cf14273795739..3425df614c8fded46c5ac4c8fe123f5f9ab5c09b 100644 | 
| --- a/base/test/scoped_feature_list.cc | 
| +++ b/base/test/scoped_feature_list.cc | 
| @@ -4,20 +4,23 @@ | 
| #include "base/test/scoped_feature_list.h" | 
| +#include <algorithm> | 
| #include <string> | 
| +#include <vector> | 
| + | 
| +#include "base/strings/string_split.h" | 
| +#include "base/strings/string_util.h" | 
| namespace base { | 
| namespace test { | 
| namespace { | 
| -static std::string GetFeatureString( | 
| +static std::vector<std::string> GetFeatureString( | 
| const std::initializer_list<base::Feature>& features) { | 
| - std::string output; | 
| + std::vector<std::string> output; | 
| for (const base::Feature& feature : features) { | 
| - if (!output.empty()) | 
| - output += ","; | 
| - output += feature.name; | 
| + output.push_back(feature.name); | 
| } | 
| return output; | 
| } | 
| @@ -43,8 +46,8 @@ void ScopedFeatureList::Init() { | 
| void ScopedFeatureList::InitWithFeatures( | 
| const std::initializer_list<base::Feature>& enabled_features, | 
| const std::initializer_list<base::Feature>& disabled_features) { | 
| 
 
Ilya Sherman
2017/04/20 20:53:07
Could this still be the main entry point?  The API
 
chaopeng
2017/04/21 02:36:21
Yes.
 
 | 
| - InitFromCommandLine(GetFeatureString(enabled_features), | 
| - GetFeatureString(disabled_features)); | 
| + InitWithFeatureListAndOverrides(GetFeatureString(enabled_features), | 
| + GetFeatureString(disabled_features)); | 
| } | 
| void ScopedFeatureList::InitWithFeatureList( | 
| @@ -63,11 +66,67 @@ void ScopedFeatureList::InitFromCommandLine( | 
| } | 
| void ScopedFeatureList::InitAndEnableFeature(const base::Feature& feature) { | 
| - InitFromCommandLine(feature.name, std::string()); | 
| + std::vector<std::string> feature_list{feature.name}; | 
| + InitWithFeatureListAndOverrides(feature_list, std::vector<std::string>()); | 
| 
 
Ilya Sherman
2017/04/20 20:53:07
nit: I think this could be simplified to
InitWith
 
chaopeng
2017/04/21 02:36:21
Done.
 
 | 
| } | 
| void ScopedFeatureList::InitAndDisableFeature(const base::Feature& feature) { | 
| - InitFromCommandLine(std::string(), feature.name); | 
| + std::vector<std::string> feature_list{feature.name}; | 
| + InitWithFeatureListAndOverrides(std::vector<std::string>(), feature_list); | 
| +} | 
| + | 
| +void OverrideFeature(const std::string& features, | 
| 
 
Ilya Sherman
2017/04/20 20:53:06
Please add documentation for this function.
 
Ilya Sherman
2017/04/20 20:53:07
nit: s/Feature/Features
 
chaopeng
2017/04/21 02:36:21
Done.
 
 | 
| + std::vector<std::string>* check_list, | 
| + std::vector<std::string>* add_to_list) { | 
| 
 
Ilya Sherman
2017/04/20 20:53:07
I'd suggest writing this method like so:
void Add
 
chaopeng
2017/04/21 02:36:21
Done.
 
 | 
| + std::vector<StringPiece> features_list = | 
| + SplitStringPiece(features, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); | 
| + | 
| + for (StringPiece& feature : features_list) { | 
| + // Start with * means use default. | 
| + if (feature.starts_with("*")) | 
| + return; | 
| 
 
Ilya Sherman
2017/04/20 20:53:07
Should this be a continue instead?  (If so, please
 
chaopeng
2017/04/21 02:36:21
Yes, we should keep it.
 
 | 
| + | 
| + // Remove field_trial info. | 
| + StringPiece feature_name = feature; | 
| + std::size_t index = feature.find("<"); | 
| + if (index != std::string::npos) | 
| + feature_name = feature.substr(0, index); | 
| 
 
Ilya Sherman
2017/04/20 20:53:07
Hmm, why is the field trial info being removed?  S
 
chaopeng
2017/04/21 02:36:21
Not remove it now.
 
 | 
| + | 
| + if (std::any_of( | 
| 
 
Ilya Sherman
2017/04/20 20:53:07
nit: std::find?
 
chaopeng
2017/04/21 02:36:21
change to count
 
 | 
| + check_list->begin(), check_list->end(), | 
| + [&feature_name](std::string& s) { return feature_name == s; })) | 
| + return; | 
| 
 
Ilya Sherman
2017/04/20 20:53:07
Ditto regarding continue vs. return.
 
chaopeng
2017/04/21 02:36:22
Done.
 
 | 
| + | 
| + if (std::any_of( | 
| + add_to_list->begin(), add_to_list->end(), | 
| + [&feature_name](std::string& s) { return feature_name == s; })) | 
| + return; | 
| 
 
Ilya Sherman
2017/04/20 20:53:07
Ditto regarding continue vs. return.
 
chaopeng
2017/04/21 02:36:21
Done.
 
 | 
| + | 
| + add_to_list->push_back(feature_name.as_string()); | 
| + } | 
| +} | 
| 
 
Ilya Sherman
2017/04/20 20:53:07
nit: Please move this into the anonymous namespace
 
chaopeng
2017/04/21 02:36:21
Done.
 
 | 
| + | 
| +void ScopedFeatureList::InitWithFeatureListAndOverrides( | 
| + const std::vector<std::string>& override_enabled_features, | 
| + const std::vector<std::string>& override_disabled_features) { | 
| + std::vector<std::string> merged_enabled_features(override_enabled_features); | 
| + std::vector<std::string> merged_disabled_features(override_disabled_features); | 
| + | 
| + base::FeatureList* feature_list = base::FeatureList::GetInstance(); | 
| + if (feature_list) { | 
| + std::string enabled_features; | 
| + std::string disabled_features; | 
| + base::FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features, | 
| + &disabled_features); | 
| + OverrideFeature(enabled_features, &merged_disabled_features, | 
| + &merged_enabled_features); | 
| + OverrideFeature(disabled_features, &merged_disabled_features, | 
| + &merged_enabled_features); | 
| 
 
Ilya Sherman
2017/04/20 20:53:07
I think the latter two args are swapped?  Please a
 
chaopeng
2017/04/21 02:36:21
Done.
 
 | 
| + } | 
| + | 
| + std::string enabled = JoinString(merged_enabled_features, ","); | 
| + std::string disabled = JoinString(merged_disabled_features, ","); | 
| + InitFromCommandLine(enabled, disabled); | 
| } | 
| } // namespace test |