flags_state_unittest.cc 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. // Copyright 2015 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/flags_ui/flags_state.h"
  5. #include <stddef.h>
  6. #include <map>
  7. #include <memory>
  8. #include <set>
  9. #include <string>
  10. #include "base/bind.h"
  11. #include "base/containers/contains.h"
  12. #include "base/feature_list.h"
  13. #include "base/format_macros.h"
  14. #include "base/metrics/field_trial.h"
  15. #include "base/strings/stringprintf.h"
  16. #include "base/strings/utf_string_conversions.h"
  17. #include "base/test/scoped_feature_list.h"
  18. #include "base/values.h"
  19. #include "build/build_config.h"
  20. #include "components/flags_ui/feature_entry.h"
  21. #include "components/flags_ui/feature_entry_macros.h"
  22. #include "components/flags_ui/flags_ui_pref_names.h"
  23. #include "components/flags_ui/flags_ui_switches.h"
  24. #include "components/flags_ui/pref_service_flags_storage.h"
  25. #include "components/prefs/pref_registry_simple.h"
  26. #include "components/prefs/testing_pref_service.h"
  27. #include "components/strings/grit/components_strings.h"
  28. #include "components/variations/variations_associated_data.h"
  29. #include "components/variations/variations_switches.h"
  30. #include "testing/gtest/include/gtest/gtest.h"
  31. namespace flags_ui {
  32. namespace {
  33. const char kFlags1[] = "flag1";
  34. const char kFlags2[] = "flag2";
  35. const char kFlags3[] = "flag3";
  36. const char kFlags4[] = "flag4";
  37. const char kFlags5[] = "flag5";
  38. const char kFlags6[] = "flag6";
  39. const char kFlags7[] = "flag7";
  40. const char kFlags8[] = "flag8";
  41. const char kFlags9[] = "flag9";
  42. const char kFlags10[] = "flag10";
  43. const char kFlags11[] = "flag11";
  44. const char kFlags12[] = "flag12";
  45. const char kSwitch1[] = "switch";
  46. const char kSwitch2[] = "switch2";
  47. const char kSwitch3[] = "switch3";
  48. const char kSwitch6[] = "switch6";
  49. const char kValueForSwitch2[] = "value_for_switch2";
  50. const char kStringSwitch[] = "string_switch";
  51. const char kValueForStringSwitch[] = "value_for_string_switch";
  52. const char kMultiSwitch1[] = "multi_switch1";
  53. const char kMultiSwitch2[] = "multi_switch2";
  54. const char kValueForMultiSwitch2[] = "value_for_multi_switch2";
  55. const char kEnableDisableValue1[] = "value1";
  56. const char kEnableDisableValue2[] = "value2";
  57. const char kEnableFeatures[] = "dummy-enable-features";
  58. const char kDisableFeatures[] = "dummy-disable-features";
  59. const char kTestTrial[] = "TestTrial";
  60. const char kTestParam1[] = "param1";
  61. const char kTestParam2[] = "param2";
  62. const char kTestParam3[] = "param:/3";
  63. const char kTestParamValue[] = "value";
  64. const base::Feature kTestFeature1{"FeatureName1",
  65. base::FEATURE_ENABLED_BY_DEFAULT};
  66. const base::Feature kTestFeature2{"FeatureName2",
  67. base::FEATURE_ENABLED_BY_DEFAULT};
  68. const base::Feature kTestFeature3{"FeatureName3",
  69. base::FEATURE_DISABLED_BY_DEFAULT};
  70. const FeatureEntry::FeatureParam kTestVariationOther1[] = {
  71. {kTestParam1, kTestParamValue}};
  72. const FeatureEntry::FeatureParam kTestVariationOther2[] = {
  73. {kTestParam2, kTestParamValue}};
  74. const FeatureEntry::FeatureParam kTestVariationOther3[] = {
  75. {kTestParam1, kTestParamValue},
  76. {kTestParam3, kTestParamValue},
  77. };
  78. const FeatureEntry::FeatureVariation kTestVariations1[] = {
  79. {"dummy description 1", kTestVariationOther1, 1, nullptr}};
  80. const FeatureEntry::FeatureVariation kTestVariations2[] = {
  81. {"dummy description 2", kTestVariationOther2, 1, nullptr}};
  82. const FeatureEntry::FeatureVariation kTestVariations3[] = {
  83. {"dummy description 1", kTestVariationOther1, 1, nullptr},
  84. {"dummy description 2", kTestVariationOther2, 1, nullptr},
  85. {"dummy description 3", kTestVariationOther3, 2, "t123456"}};
  86. const char kTestVariation3Cmdline[] =
  87. "FeatureName3:param1/value/param%3A%2F3/value";
  88. const char kDummyName[] = "";
  89. const char kDummyDescription[] = "";
  90. bool SkipFeatureEntry(const FeatureEntry& feature_entry) {
  91. return false;
  92. }
  93. } // namespace
  94. const FeatureEntry::Choice kMultiChoices[] = {
  95. {kDummyDescription, "", ""},
  96. {kDummyDescription, kMultiSwitch1, ""},
  97. {kDummyDescription, kMultiSwitch2, kValueForMultiSwitch2},
  98. };
  99. // The entries that are set for these tests. The 3rd entry is not supported on
  100. // the current platform, all others are.
  101. static FeatureEntry kEntries[] = {
  102. {kFlags1, kDummyName, kDummyDescription,
  103. 0, // Ends up being mapped to the current platform.
  104. SINGLE_VALUE_TYPE(kSwitch1)},
  105. {kFlags2, kDummyName, kDummyDescription,
  106. 0, // Ends up being mapped to the current platform.
  107. SINGLE_VALUE_TYPE_AND_VALUE(kSwitch2, kValueForSwitch2)},
  108. {kFlags3, kDummyName, kDummyDescription,
  109. 0, // This ends up enabling for an OS other than the current.
  110. SINGLE_VALUE_TYPE(kSwitch3)},
  111. {kFlags4, kDummyName, kDummyDescription,
  112. 0, // Ends up being mapped to the current platform.
  113. MULTI_VALUE_TYPE(kMultiChoices)},
  114. {kFlags5, kDummyName, kDummyDescription,
  115. 0, // Ends up being mapped to the current platform.
  116. ENABLE_DISABLE_VALUE_TYPE_AND_VALUE(kSwitch1,
  117. kEnableDisableValue1,
  118. kSwitch2,
  119. kEnableDisableValue2)},
  120. {kFlags6, kDummyName, kDummyDescription, 0,
  121. SINGLE_DISABLE_VALUE_TYPE(kSwitch6)},
  122. {kFlags7, kDummyName, kDummyDescription,
  123. 0, // Ends up being mapped to the current platform.
  124. FEATURE_VALUE_TYPE(kTestFeature1)},
  125. {kFlags8, kDummyName, kDummyDescription,
  126. 0, // Ends up being mapped to the current platform.
  127. FEATURE_WITH_PARAMS_VALUE_TYPE(kTestFeature1,
  128. kTestVariations1,
  129. kTestTrial)},
  130. {kFlags9, kDummyName, kDummyDescription,
  131. 0, // Ends up being mapped to the current platform.
  132. FEATURE_WITH_PARAMS_VALUE_TYPE(kTestFeature1,
  133. kTestVariations1,
  134. kTestTrial)},
  135. {kFlags10, kDummyName, kDummyDescription,
  136. 0, // Ends up being mapped to the current platform.
  137. FEATURE_WITH_PARAMS_VALUE_TYPE(kTestFeature2,
  138. kTestVariations2,
  139. kTestTrial)},
  140. {kFlags11, kDummyName, kDummyDescription,
  141. 0, // Ends up being mapped to the current platform.
  142. ORIGIN_LIST_VALUE_TYPE(kStringSwitch, kValueForStringSwitch)},
  143. {kFlags12, kDummyName, kDummyDescription,
  144. 0, // Ends up being mapped to the current platform.
  145. FEATURE_WITH_PARAMS_VALUE_TYPE(kTestFeature3,
  146. kTestVariations3,
  147. kTestTrial)}};
  148. class FlagsStateTest : public ::testing::Test,
  149. public flags_ui::FlagsState::Delegate {
  150. protected:
  151. FlagsStateTest() : flags_storage_(&prefs_) {
  152. prefs_.registry()->RegisterListPref(prefs::kAboutFlagsEntries);
  153. prefs_.registry()->RegisterDictionaryPref(prefs::kAboutFlagsOriginLists);
  154. for (size_t i = 0; i < std::size(kEntries); ++i)
  155. kEntries[i].supported_platforms = FlagsState::GetCurrentPlatform();
  156. int os_other_than_current = 1;
  157. while (os_other_than_current == FlagsState::GetCurrentPlatform())
  158. os_other_than_current <<= 1;
  159. kEntries[2].supported_platforms = os_other_than_current;
  160. flags_state_ = std::make_unique<FlagsState>(kEntries, this);
  161. }
  162. ~FlagsStateTest() override {
  163. variations::testing::ClearAllVariationParams();
  164. }
  165. // FlagsState::Delegate:
  166. bool ShouldExcludeFlag(const FlagsStorage* storage,
  167. const FeatureEntry& entry) override {
  168. return exclude_flags_.count(entry.internal_name) != 0;
  169. }
  170. TestingPrefServiceSimple prefs_;
  171. PrefServiceFlagsStorage flags_storage_;
  172. std::unique_ptr<FlagsState> flags_state_;
  173. std::set<std::string> exclude_flags_;
  174. };
  175. TEST_F(FlagsStateTest, NoChangeNoRestart) {
  176. EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
  177. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, false);
  178. EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
  179. // kFlags6 is enabled by default, so enabling should not require a restart.
  180. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags6, true);
  181. EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
  182. }
  183. TEST_F(FlagsStateTest, ChangeNeedsRestart) {
  184. EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
  185. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
  186. EXPECT_TRUE(flags_state_->IsRestartNeededToCommitChanges());
  187. }
  188. // Tests that disabling a default enabled entry requires a restart.
  189. TEST_F(FlagsStateTest, DisableChangeNeedsRestart) {
  190. EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
  191. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags6, false);
  192. EXPECT_TRUE(flags_state_->IsRestartNeededToCommitChanges());
  193. }
  194. TEST_F(FlagsStateTest, MultiFlagChangeNeedsRestart) {
  195. const FeatureEntry& entry = kEntries[3];
  196. ASSERT_EQ(kFlags4, entry.internal_name);
  197. EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
  198. // Enable the 2nd choice of the multi-value.
  199. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
  200. true);
  201. EXPECT_TRUE(flags_state_->IsRestartNeededToCommitChanges());
  202. flags_state_->Reset();
  203. EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
  204. // Enable the default choice now.
  205. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
  206. true);
  207. EXPECT_TRUE(flags_state_->IsRestartNeededToCommitChanges());
  208. }
  209. TEST_F(FlagsStateTest, AddTwoFlagsRemoveOne) {
  210. // Add two entries, check they're there.
  211. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
  212. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, true);
  213. {
  214. const base::Value::List& entries_list =
  215. prefs_.GetValueList(prefs::kAboutFlagsEntries);
  216. ASSERT_EQ(2u, entries_list.size());
  217. std::string s0 = entries_list[0].GetString();
  218. std::string s1 = entries_list[1].GetString();
  219. EXPECT_TRUE(s0 == kFlags1 || s1 == kFlags1);
  220. EXPECT_TRUE(s0 == kFlags2 || s1 == kFlags2);
  221. }
  222. // Remove one entry, check the other's still around.
  223. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, false);
  224. {
  225. const base::Value::List& entries_list =
  226. prefs_.GetValueList(prefs::kAboutFlagsEntries);
  227. ASSERT_EQ(1u, entries_list.size());
  228. std::string s0 = entries_list[0].GetString();
  229. EXPECT_TRUE(s0 == kFlags1);
  230. }
  231. }
  232. TEST_F(FlagsStateTest, AddTwoFlagsRemoveBoth) {
  233. // Add two entries, check the pref exists.
  234. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
  235. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, true);
  236. {
  237. const base::Value::List& entries_list =
  238. prefs_.GetValueList(prefs::kAboutFlagsEntries);
  239. ASSERT_EQ(2u, entries_list.size());
  240. }
  241. // Remove both, the pref should have been removed completely.
  242. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, false);
  243. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, false);
  244. {
  245. const base::Value::List& entries_list =
  246. prefs_.GetValueList(prefs::kAboutFlagsEntries);
  247. EXPECT_TRUE(entries_list.empty());
  248. }
  249. }
  250. TEST_F(FlagsStateTest, CombineOriginListValues) {
  251. // Add a value in prefs, and on command line.
  252. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags11, true);
  253. const std::string prefs_value =
  254. "http://a.test,http://c.test,http://dupe.test";
  255. flags_state_->SetOriginListFlag(kFlags11, prefs_value, &flags_storage_);
  256. ASSERT_EQ(flags_storage_.GetOriginListFlag(kFlags11), prefs_value);
  257. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  258. const std::string cli_value = "http://dupe.test,http://b.test";
  259. command_line.AppendSwitchASCII(kStringSwitch, cli_value);
  260. ASSERT_EQ(command_line.GetSwitchValueASCII(kStringSwitch), cli_value);
  261. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  262. kNoSentinels, kEnableFeatures,
  263. kDisableFeatures);
  264. // Lists are concatenated together with duplicates removed, but are not
  265. // sorted.
  266. EXPECT_EQ(command_line.GetSwitchValueASCII(kStringSwitch),
  267. "http://dupe.test,http://b.test,http://a.test,http://c.test");
  268. }
  269. TEST_F(FlagsStateTest, ConvertFlagsToSwitches) {
  270. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
  271. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  272. command_line.AppendSwitch("foo");
  273. EXPECT_TRUE(command_line.HasSwitch("foo"));
  274. EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
  275. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  276. kAddSentinels, kEnableFeatures,
  277. kDisableFeatures);
  278. EXPECT_TRUE(command_line.HasSwitch("foo"));
  279. EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
  280. EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesBegin));
  281. EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesEnd));
  282. base::CommandLine command_line2(base::CommandLine::NO_PROGRAM);
  283. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line2,
  284. kNoSentinels, kEnableFeatures,
  285. kDisableFeatures);
  286. EXPECT_TRUE(command_line2.HasSwitch(kSwitch1));
  287. EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesBegin));
  288. EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesEnd));
  289. base::CommandLine command_line3(base::CommandLine::NO_PROGRAM);
  290. // Enable 3rd variation (@4 since 0 is enable).
  291. flags_state_->SetFeatureEntryEnabled(
  292. &flags_storage_, std::string(kFlags12).append("@4"), true);
  293. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line3,
  294. kNoSentinels, kEnableFeatures,
  295. kDisableFeatures);
  296. EXPECT_TRUE(command_line3.HasSwitch(kEnableFeatures));
  297. EXPECT_EQ(command_line3.GetSwitchValueASCII(kEnableFeatures),
  298. kTestVariation3Cmdline);
  299. EXPECT_TRUE(
  300. command_line3.HasSwitch(variations::switches::kForceVariationIds));
  301. EXPECT_EQ(command_line3.GetSwitchValueASCII(
  302. variations::switches::kForceVariationIds),
  303. "t123456");
  304. }
  305. TEST_F(FlagsStateTest, RegisterAllFeatureVariationParameters) {
  306. const FeatureEntry& entry = kEntries[7];
  307. std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
  308. // Select the "Default" variation.
  309. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
  310. true);
  311. flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
  312. feature_list.get());
  313. // No value should be associated.
  314. EXPECT_EQ("", variations::GetVariationParamValue(kTestTrial, kTestParam1));
  315. // The trial should not be created.
  316. base::FieldTrial* trial = base::FieldTrialList::Find(kTestTrial);
  317. EXPECT_EQ(nullptr, trial);
  318. // Select the default "Enabled" variation.
  319. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(1),
  320. true);
  321. flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
  322. feature_list.get());
  323. // No value should be associated as this is the default option.
  324. EXPECT_EQ("", variations::GetVariationParamValue(kTestTrial, kTestParam1));
  325. // The trial should be created.
  326. trial = base::FieldTrialList::Find(kTestTrial);
  327. EXPECT_NE(nullptr, trial);
  328. // The about:flags group should be selected for the trial.
  329. EXPECT_EQ(internal::kTrialGroupAboutFlags, trial->group_name());
  330. // Select the only one variation.
  331. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
  332. true);
  333. flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
  334. feature_list.get());
  335. // Associating for the second time should not change the value.
  336. EXPECT_EQ("", variations::GetVariationParamValue(kTestTrial, kTestParam1));
  337. }
  338. TEST_F(FlagsStateTest, RegisterAllFeatureVariationParametersNonDefault) {
  339. const FeatureEntry& entry = kEntries[7];
  340. std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
  341. // Select the only one variation.
  342. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
  343. true);
  344. flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
  345. feature_list.get());
  346. // Set the feature_list as the main instance so that
  347. // variations::GetVariationParamValueByFeature below works.
  348. base::test::ScopedFeatureList scoped_feature_list;
  349. scoped_feature_list.InitWithFeatureList(std::move(feature_list));
  350. // The param should have the value predefined in this variation.
  351. EXPECT_EQ(kTestParamValue,
  352. variations::GetVariationParamValue(kTestTrial, kTestParam1));
  353. // The value should be associated also via the name of the feature.
  354. EXPECT_EQ(kTestParamValue, variations::GetVariationParamValueByFeature(
  355. kTestFeature1, kTestParam1));
  356. }
  357. TEST_F(FlagsStateTest, RegisterAllFeatureVariationParametersWithDefaultTrials) {
  358. const FeatureEntry& entry1 = kEntries[8];
  359. const FeatureEntry& entry2 = kEntries[9];
  360. std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
  361. // Select the only one variation for each FeatureEntry.
  362. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry1.NameForOption(2),
  363. true);
  364. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry2.NameForOption(2),
  365. true);
  366. flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
  367. feature_list.get());
  368. // Set the feature_list as the main instance so that
  369. // variations::GetVariationParamValueByFeature below works.
  370. base::test::ScopedFeatureList scoped_feature_list;
  371. scoped_feature_list.InitWithFeatureList(std::move(feature_list));
  372. // The params should have the values predefined in these variations
  373. // (accessible via the names of the features).
  374. EXPECT_EQ(kTestParamValue, variations::GetVariationParamValueByFeature(
  375. kTestFeature1, kTestParam1));
  376. EXPECT_EQ(kTestParamValue, variations::GetVariationParamValueByFeature(
  377. kTestFeature2, kTestParam2));
  378. // The params are registered in the same trial.
  379. EXPECT_EQ(kTestParamValue,
  380. variations::GetVariationParamValue(kTestTrial, kTestParam1));
  381. EXPECT_EQ(kTestParamValue,
  382. variations::GetVariationParamValue(kTestTrial, kTestParam2));
  383. }
  384. base::CommandLine::StringType CreateSwitch(const std::string& value) {
  385. #if BUILDFLAG(IS_WIN)
  386. return base::ASCIIToWide(value);
  387. #else
  388. return value;
  389. #endif
  390. }
  391. TEST_F(FlagsStateTest, RemoveFlagSwitches) {
  392. base::CommandLine::SwitchMap switch_list;
  393. switch_list[kSwitch1] = base::CommandLine::StringType();
  394. switch_list[switches::kFlagSwitchesBegin] = base::CommandLine::StringType();
  395. switch_list[switches::kFlagSwitchesEnd] = base::CommandLine::StringType();
  396. switch_list["foo"] = base::CommandLine::StringType();
  397. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
  398. // This shouldn't do anything before ConvertFlagsToSwitches() wasn't called.
  399. flags_state_->RemoveFlagsSwitches(&switch_list);
  400. ASSERT_EQ(4u, switch_list.size());
  401. EXPECT_TRUE(base::Contains(switch_list, kSwitch1));
  402. EXPECT_TRUE(base::Contains(switch_list, switches::kFlagSwitchesBegin));
  403. EXPECT_TRUE(base::Contains(switch_list, switches::kFlagSwitchesEnd));
  404. EXPECT_TRUE(base::Contains(switch_list, "foo"));
  405. // Call ConvertFlagsToSwitches(), then RemoveFlagsSwitches() again.
  406. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  407. command_line.AppendSwitch("foo");
  408. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  409. kAddSentinels, kEnableFeatures,
  410. kDisableFeatures);
  411. flags_state_->RemoveFlagsSwitches(&switch_list);
  412. // Now the about:flags-related switch should have been removed.
  413. ASSERT_EQ(1u, switch_list.size());
  414. EXPECT_TRUE(base::Contains(switch_list, "foo"));
  415. }
  416. TEST_F(FlagsStateTest, RemoveFlagSwitches_Features) {
  417. struct {
  418. int enabled_choice; // 0: default, 1: enabled, 2: disabled.
  419. const char* existing_enable_features;
  420. const char* existing_disable_features;
  421. const char* expected_enable_features;
  422. const char* expected_disable_features;
  423. } cases[] = {
  424. // Default value: Should not affect existing flags.
  425. {0, nullptr, nullptr, nullptr, nullptr},
  426. {0, "A,B", "C", "A,B", "C"},
  427. // "Enable" option: should only affect enabled list.
  428. {1, nullptr, nullptr, "FeatureName1", nullptr},
  429. {1, "A,B", "C", "A,B,FeatureName1", "C"},
  430. // "Disable" option: should only affect disabled list.
  431. {2, nullptr, nullptr, nullptr, "FeatureName1"},
  432. {2, "A,B", "C", "A,B", "C,FeatureName1"},
  433. };
  434. for (size_t i = 0; i < std::size(cases); ++i) {
  435. SCOPED_TRACE(base::StringPrintf(
  436. "Test[%" PRIuS "]: %d [%s] [%s]", i, cases[i].enabled_choice,
  437. cases[i].existing_enable_features ? cases[i].existing_enable_features
  438. : "null",
  439. cases[i].existing_disable_features ? cases[i].existing_disable_features
  440. : "null"));
  441. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  442. if (cases[i].existing_enable_features) {
  443. command_line.AppendSwitchASCII(kEnableFeatures,
  444. cases[i].existing_enable_features);
  445. }
  446. if (cases[i].existing_disable_features) {
  447. command_line.AppendSwitchASCII(kDisableFeatures,
  448. cases[i].existing_disable_features);
  449. }
  450. flags_state_->Reset();
  451. const std::string entry_name = base::StringPrintf(
  452. "%s%s%d", kFlags7, testing::kMultiSeparator, cases[i].enabled_choice);
  453. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry_name, true);
  454. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  455. kAddSentinels, kEnableFeatures,
  456. kDisableFeatures);
  457. auto switch_list = command_line.GetSwitches();
  458. EXPECT_EQ(cases[i].expected_enable_features != nullptr,
  459. base::Contains(switch_list, kEnableFeatures));
  460. if (cases[i].expected_enable_features)
  461. EXPECT_EQ(CreateSwitch(cases[i].expected_enable_features),
  462. switch_list[kEnableFeatures]);
  463. EXPECT_EQ(cases[i].expected_disable_features != nullptr,
  464. base::Contains(switch_list, kDisableFeatures));
  465. if (cases[i].expected_disable_features)
  466. EXPECT_EQ(CreateSwitch(cases[i].expected_disable_features),
  467. switch_list[kDisableFeatures]);
  468. // RemoveFlagsSwitches() should result in the original values for these
  469. // switches.
  470. switch_list = command_line.GetSwitches();
  471. flags_state_->RemoveFlagsSwitches(&switch_list);
  472. EXPECT_EQ(cases[i].existing_enable_features != nullptr,
  473. base::Contains(switch_list, kEnableFeatures));
  474. if (cases[i].existing_enable_features)
  475. EXPECT_EQ(CreateSwitch(cases[i].existing_enable_features),
  476. switch_list[kEnableFeatures]);
  477. EXPECT_EQ(cases[i].existing_disable_features != nullptr,
  478. base::Contains(switch_list, kEnableFeatures));
  479. if (cases[i].existing_disable_features)
  480. EXPECT_EQ(CreateSwitch(cases[i].existing_disable_features),
  481. switch_list[kDisableFeatures]);
  482. }
  483. }
  484. // Tests enabling entries that aren't supported on the current platform.
  485. TEST_F(FlagsStateTest, PersistAndPrune) {
  486. // Enable entries 1 and 3.
  487. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
  488. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags3, true);
  489. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  490. EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
  491. EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
  492. // Convert the flags to switches. Entry 3 shouldn't be among the switches
  493. // as it is not applicable to the current platform.
  494. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  495. kAddSentinels, kEnableFeatures,
  496. kDisableFeatures);
  497. EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
  498. EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
  499. // FeatureEntry 3 should show still be persisted in preferences though.
  500. const base::Value::List& entries_list =
  501. prefs_.GetValueList(prefs::kAboutFlagsEntries);
  502. EXPECT_EQ(2U, entries_list.size());
  503. std::string s0 = entries_list[0].GetString();
  504. EXPECT_EQ(kFlags1, s0);
  505. std::string s1 = entries_list[1].GetString();
  506. EXPECT_EQ(kFlags3, s1);
  507. }
  508. // Tests that switches which should have values get them in the command
  509. // line.
  510. TEST_F(FlagsStateTest, CheckValues) {
  511. // Enable entries 1 and 2.
  512. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
  513. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, true);
  514. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  515. EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
  516. EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
  517. // Convert the flags to switches.
  518. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  519. kAddSentinels, kEnableFeatures,
  520. kDisableFeatures);
  521. EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
  522. EXPECT_EQ(std::string(), command_line.GetSwitchValueASCII(kSwitch1));
  523. EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
  524. EXPECT_EQ(std::string(kValueForSwitch2),
  525. command_line.GetSwitchValueASCII(kSwitch2));
  526. // Confirm that there is no '=' in the command line for simple switches.
  527. std::string switch1_with_equals =
  528. std::string("--") + std::string(kSwitch1) + std::string("=");
  529. #if BUILDFLAG(IS_WIN)
  530. EXPECT_EQ(std::wstring::npos, command_line.GetCommandLineString().find(
  531. base::ASCIIToWide(switch1_with_equals)));
  532. #else
  533. EXPECT_EQ(std::string::npos,
  534. command_line.GetCommandLineString().find(switch1_with_equals));
  535. #endif
  536. // And confirm there is a '=' for switches with values.
  537. std::string switch2_with_equals =
  538. std::string("--") + std::string(kSwitch2) + std::string("=");
  539. #if BUILDFLAG(IS_WIN)
  540. EXPECT_NE(std::wstring::npos, command_line.GetCommandLineString().find(
  541. base::ASCIIToWide(switch2_with_equals)));
  542. #else
  543. EXPECT_NE(std::string::npos,
  544. command_line.GetCommandLineString().find(switch2_with_equals));
  545. #endif
  546. // And it should persist.
  547. const base::Value::List& entries_list =
  548. prefs_.GetValueList(prefs::kAboutFlagsEntries);
  549. EXPECT_EQ(2U, entries_list.size());
  550. std::string s0 = entries_list[0].GetString();
  551. EXPECT_EQ(kFlags1, s0);
  552. std::string s1 = entries_list[1].GetString();
  553. EXPECT_EQ(kFlags2, s1);
  554. }
  555. // Tests multi-value type entries.
  556. TEST_F(FlagsStateTest, MultiValues) {
  557. const FeatureEntry& entry = kEntries[3];
  558. ASSERT_EQ(kFlags4, entry.internal_name);
  559. // Initially, the first "deactivated" option of the multi entry should
  560. // be set.
  561. {
  562. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  563. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  564. kAddSentinels, kEnableFeatures,
  565. kDisableFeatures);
  566. EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
  567. EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
  568. }
  569. // Enable the 2nd choice of the multi-value.
  570. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
  571. true);
  572. {
  573. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  574. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  575. kAddSentinels, kEnableFeatures,
  576. kDisableFeatures);
  577. EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
  578. EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch2));
  579. EXPECT_EQ(std::string(kValueForMultiSwitch2),
  580. command_line.GetSwitchValueASCII(kMultiSwitch2));
  581. }
  582. // Disable the multi-value entry.
  583. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
  584. true);
  585. {
  586. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  587. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  588. kAddSentinels, kEnableFeatures,
  589. kDisableFeatures);
  590. EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
  591. EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
  592. }
  593. }
  594. // Tests that disable flags are added when an entry is disabled.
  595. TEST_F(FlagsStateTest, DisableFlagCommandLine) {
  596. // Nothing selected.
  597. {
  598. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  599. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  600. kAddSentinels, kEnableFeatures,
  601. kDisableFeatures);
  602. EXPECT_FALSE(command_line.HasSwitch(kSwitch6));
  603. }
  604. // Disable the entry 6.
  605. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags6, false);
  606. {
  607. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  608. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  609. kAddSentinels, kEnableFeatures,
  610. kDisableFeatures);
  611. EXPECT_TRUE(command_line.HasSwitch(kSwitch6));
  612. }
  613. // Enable entry 6.
  614. flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags6, true);
  615. {
  616. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  617. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  618. kAddSentinels, kEnableFeatures,
  619. kDisableFeatures);
  620. EXPECT_FALSE(command_line.HasSwitch(kSwitch6));
  621. }
  622. }
  623. TEST_F(FlagsStateTest, EnableDisableValues) {
  624. const FeatureEntry& entry = kEntries[4];
  625. ASSERT_EQ(kFlags5, entry.internal_name);
  626. // Nothing selected.
  627. {
  628. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  629. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  630. kAddSentinels, kEnableFeatures,
  631. kDisableFeatures);
  632. EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
  633. EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
  634. }
  635. // "Enable" option selected.
  636. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(1),
  637. true);
  638. {
  639. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  640. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  641. kAddSentinels, kEnableFeatures,
  642. kDisableFeatures);
  643. EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
  644. EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
  645. EXPECT_EQ(kEnableDisableValue1, command_line.GetSwitchValueASCII(kSwitch1));
  646. }
  647. // "Disable" option selected.
  648. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
  649. true);
  650. {
  651. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  652. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  653. kAddSentinels, kEnableFeatures,
  654. kDisableFeatures);
  655. EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
  656. EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
  657. EXPECT_EQ(kEnableDisableValue2, command_line.GetSwitchValueASCII(kSwitch2));
  658. }
  659. // "Default" option selected, same as nothing selected.
  660. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
  661. true);
  662. {
  663. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  664. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  665. kAddSentinels, kEnableFeatures,
  666. kDisableFeatures);
  667. EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
  668. EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
  669. }
  670. // "Disable" option selected, but flag filtered out by exclude predicate.
  671. flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
  672. true);
  673. exclude_flags_.insert(entry.internal_name);
  674. {
  675. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  676. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  677. kAddSentinels, kEnableFeatures,
  678. kDisableFeatures);
  679. EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
  680. EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
  681. }
  682. exclude_flags_.clear();
  683. }
  684. TEST_F(FlagsStateTest, FeatureValues) {
  685. const FeatureEntry& entry = kEntries[6];
  686. ASSERT_EQ(kFlags7, entry.internal_name);
  687. struct {
  688. int enabled_choice;
  689. const char* existing_enable_features;
  690. const char* existing_disable_features;
  691. const char* expected_enable_features;
  692. const char* expected_disable_features;
  693. } cases[] = {
  694. // Nothing selected.
  695. {-1, nullptr, nullptr, "", ""},
  696. // "Default" option selected, same as nothing selected.
  697. {0, nullptr, nullptr, "", ""},
  698. // "Enable" option selected.
  699. {1, nullptr, nullptr, "FeatureName1", ""},
  700. // "Disable" option selected.
  701. {2, nullptr, nullptr, "", "FeatureName1"},
  702. // "Enable" option should get added to the existing list.
  703. {1, "Foo,Bar", nullptr, "Foo,Bar,FeatureName1", ""},
  704. // "Disable" option should get added to the existing list.
  705. {2, nullptr, "Foo,Bar", "", "Foo,Bar,FeatureName1"},
  706. };
  707. for (size_t i = 0; i < std::size(cases); ++i) {
  708. SCOPED_TRACE(base::StringPrintf(
  709. "Test[%" PRIuS "]: %d [%s] [%s]", i, cases[i].enabled_choice,
  710. cases[i].existing_enable_features ? cases[i].existing_enable_features
  711. : "null",
  712. cases[i].existing_disable_features ? cases[i].existing_disable_features
  713. : "null"));
  714. if (cases[i].enabled_choice != -1) {
  715. flags_state_->SetFeatureEntryEnabled(
  716. &flags_storage_, entry.NameForOption(cases[i].enabled_choice), true);
  717. }
  718. base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  719. if (cases[i].existing_enable_features) {
  720. command_line.AppendSwitchASCII(kEnableFeatures,
  721. cases[i].existing_enable_features);
  722. }
  723. if (cases[i].existing_disable_features) {
  724. command_line.AppendSwitchASCII(kDisableFeatures,
  725. cases[i].existing_disable_features);
  726. }
  727. flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
  728. kAddSentinels, kEnableFeatures,
  729. kDisableFeatures);
  730. EXPECT_EQ(cases[i].expected_enable_features,
  731. command_line.GetSwitchValueASCII(kEnableFeatures));
  732. EXPECT_EQ(cases[i].expected_disable_features,
  733. command_line.GetSwitchValueASCII(kDisableFeatures));
  734. }
  735. }
  736. TEST_F(FlagsStateTest, GetFlagFeatureEntries) {
  737. base::Value::List supported_entries;
  738. base::Value::List unsupported_entries;
  739. flags_state_->GetFlagFeatureEntries(&flags_storage_, kGeneralAccessFlagsOnly,
  740. supported_entries, unsupported_entries,
  741. base::BindRepeating(&SkipFeatureEntry));
  742. // All |kEntries| except for |kFlags3| should be supported.
  743. auto supported_count = supported_entries.size();
  744. auto unsupported_count = unsupported_entries.size();
  745. EXPECT_EQ(11u, supported_count);
  746. EXPECT_EQ(1u, unsupported_count);
  747. EXPECT_EQ(std::size(kEntries), supported_count + unsupported_count);
  748. }
  749. } // namespace flags_ui