blob: 57893422d9d5f14ac74dbc25056277ae9ff27cc0 [file] [log] [blame]
sdefresne0e566342015-11-24 08:55:461// 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
5#include "components/flags_ui/flags_state.h"
6
avibc5337b2015-12-25 23:16:337#include <stddef.h>
8
sdefresne0e566342015-11-24 08:55:469#include <map>
dcheng84c358e2016-04-26 07:05:5310#include <memory>
sdefresne0e566342015-11-24 08:55:4611#include <set>
12#include <string>
13
14#include "base/bind.h"
15#include "base/feature_list.h"
16#include "base/format_macros.h"
avibc5337b2015-12-25 23:16:3317#include "base/macros.h"
jkrcal1383d1d2016-06-17 12:40:5618#include "base/metrics/field_trial.h"
sdefresne0e566342015-11-24 08:55:4619#include "base/stl_util.h"
20#include "base/strings/stringprintf.h"
21#include "base/strings/utf_string_conversions.h"
asvitkine9499b8d2016-08-09 05:37:0722#include "base/test/scoped_feature_list.h"
sdefresne0e566342015-11-24 08:55:4623#include "base/values.h"
avibc5337b2015-12-25 23:16:3324#include "build/build_config.h"
sdefresne0e566342015-11-24 08:55:4625#include "components/flags_ui/feature_entry.h"
26#include "components/flags_ui/flags_ui_pref_names.h"
27#include "components/flags_ui/flags_ui_switches.h"
28#include "components/flags_ui/pref_service_flags_storage.h"
brettw066508682016-02-03 08:22:0229#include "components/prefs/pref_registry_simple.h"
30#include "components/prefs/testing_pref_service.h"
jkrcal1383d1d2016-06-17 12:40:5631#include "components/variations/variations_associated_data.h"
sdefresne0e566342015-11-24 08:55:4632#include "grit/components_strings.h"
33#include "testing/gtest/include/gtest/gtest.h"
34
35namespace flags_ui {
36
37namespace {
38
39const char kFlags1[] = "flag1";
40const char kFlags2[] = "flag2";
41const char kFlags3[] = "flag3";
42const char kFlags4[] = "flag4";
43const char kFlags5[] = "flag5";
44const char kFlags6[] = "flag6";
45const char kFlags7[] = "flag7";
jkrcal1383d1d2016-06-17 12:40:5646const char kFlags8[] = "flag8";
sdefresne0e566342015-11-24 08:55:4647
48const char kSwitch1[] = "switch";
49const char kSwitch2[] = "switch2";
50const char kSwitch3[] = "switch3";
51const char kSwitch6[] = "switch6";
52const char kValueForSwitch2[] = "value_for_switch2";
53
54const char kMultiSwitch1[] = "multi_switch1";
55const char kMultiSwitch2[] = "multi_switch2";
56const char kValueForMultiSwitch2[] = "value_for_multi_switch2";
57
58const char kEnableDisableValue1[] = "value1";
59const char kEnableDisableValue2[] = "value2";
60
61const char kEnableFeatures[] = "dummy-enable-features";
62const char kDisableFeatures[] = "dummy-disable-features";
63
jkrcal1383d1d2016-06-17 12:40:5664const char kTestTrial[] = "TestTrial";
65const char kTestParam[] = "param";
jkrcal05f7ce32016-07-13 16:42:0966const char kTestParamValue[] = "value";
jkrcal1383d1d2016-06-17 12:40:5667
68const base::Feature kTestFeature1{"FeatureName1",
69 base::FEATURE_ENABLED_BY_DEFAULT};
70const base::Feature kTestFeature2{"FeatureName2",
71 base::FEATURE_ENABLED_BY_DEFAULT};
72
jkrcal1383d1d2016-06-17 12:40:5673const FeatureEntry::FeatureParam kTestVariationOther[] = {
jkrcal05f7ce32016-07-13 16:42:0974 {kTestParam, kTestParamValue}};
jkrcal1383d1d2016-06-17 12:40:5675
76const FeatureEntry::FeatureVariation kTestVariations[] = {
jkrcalbf073372016-07-29 07:21:3177 {"dummy description", kTestVariationOther, 1, nullptr}};
jkrcal1383d1d2016-06-17 12:40:5678
sdefresne0e566342015-11-24 08:55:4679// Those have to be valid ids for the translation system but the value are
80// never used, so pick one at random from the current component.
81const int kDummyNameId = IDS_FLAGS_UI_WARNING_HEADER;
82const int kDummyDescriptionId = IDS_FLAGS_UI_WARNING_TEXT;
83
84bool SkipFeatureEntry(const FeatureEntry& feature_entry) {
85 return false;
86}
87
88} // namespace
89
90const FeatureEntry::Choice kMultiChoices[] = {
91 {kDummyDescriptionId, "", ""},
92 {kDummyDescriptionId, kMultiSwitch1, ""},
93 {kDummyDescriptionId, kMultiSwitch2, kValueForMultiSwitch2},
94};
95
sdefresne0e566342015-11-24 08:55:4696// The entries that are set for these tests. The 3rd entry is not supported on
97// the current platform, all others are.
98static FeatureEntry kEntries[] = {
99 {kFlags1, kDummyNameId, kDummyDescriptionId,
100 0, // Ends up being mapped to the current platform.
jkrcal1383d1d2016-06-17 12:40:56101 FeatureEntry::SINGLE_VALUE, kSwitch1, "", nullptr, nullptr, nullptr, 0,
102 nullptr, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46103 {kFlags2, kDummyNameId, kDummyDescriptionId,
104 0, // Ends up being mapped to the current platform.
105 FeatureEntry::SINGLE_VALUE, kSwitch2, kValueForSwitch2, nullptr, nullptr,
jkrcal1383d1d2016-06-17 12:40:56106 nullptr, 0, nullptr, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46107 {kFlags3, kDummyNameId, kDummyDescriptionId,
108 0, // This ends up enabling for an OS other than the current.
jkrcal1383d1d2016-06-17 12:40:56109 FeatureEntry::SINGLE_VALUE, kSwitch3, "", nullptr, nullptr, nullptr, 0,
110 nullptr, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46111 {kFlags4, kDummyNameId, kDummyDescriptionId,
112 0, // Ends up being mapped to the current platform.
jkrcal1383d1d2016-06-17 12:40:56113 FeatureEntry::MULTI_VALUE, "", "", "", "", nullptr,
114 arraysize(kMultiChoices), kMultiChoices, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46115 {kFlags5, kDummyNameId, kDummyDescriptionId,
116 0, // Ends up being mapped to the current platform.
117 FeatureEntry::ENABLE_DISABLE_VALUE, kSwitch1, kEnableDisableValue1,
jkrcal1383d1d2016-06-17 12:40:56118 kSwitch2, kEnableDisableValue2, nullptr, 3, nullptr, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46119 {kFlags6, kDummyNameId, kDummyDescriptionId, 0,
120 FeatureEntry::SINGLE_DISABLE_VALUE, kSwitch6, "", nullptr, nullptr,
jkrcal1383d1d2016-06-17 12:40:56121 nullptr, 0, nullptr, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46122 {kFlags7, kDummyNameId, kDummyDescriptionId,
123 0, // Ends up being mapped to the current platform.
124 FeatureEntry::FEATURE_VALUE, nullptr, nullptr, nullptr, nullptr,
jkrcal1383d1d2016-06-17 12:40:56125 &kTestFeature1, 3, nullptr, nullptr, nullptr},
126 {kFlags8, kDummyNameId, kDummyDescriptionId,
127 0, // Ends up being mapped to the current platform.
128 FeatureEntry::FEATURE_WITH_VARIATIONS_VALUE, nullptr, nullptr, nullptr,
129 nullptr, &kTestFeature2, 4, nullptr, kTestVariations, kTestTrial},
sdefresne0e566342015-11-24 08:55:46130};
131
132class FlagsStateTest : public ::testing::Test {
133 protected:
jkrcal1383d1d2016-06-17 12:40:56134 FlagsStateTest() : flags_storage_(&prefs_), trial_list_(nullptr) {
sdefresne0e566342015-11-24 08:55:46135 prefs_.registry()->RegisterListPref(prefs::kEnabledLabsExperiments);
136
137 for (size_t i = 0; i < arraysize(kEntries); ++i)
138 kEntries[i].supported_platforms = FlagsState::GetCurrentPlatform();
139
140 int os_other_than_current = 1;
141 while (os_other_than_current == FlagsState::GetCurrentPlatform())
142 os_other_than_current <<= 1;
143 kEntries[2].supported_platforms = os_other_than_current;
144 flags_state_.reset(new FlagsState(kEntries, arraysize(kEntries)));
145 }
146
jkrcal05f7ce32016-07-13 16:42:09147 ~FlagsStateTest() override {
148 variations::testing::ClearAllVariationParams();
149 }
150
sdefresne0e566342015-11-24 08:55:46151 TestingPrefServiceSimple prefs_;
152 PrefServiceFlagsStorage flags_storage_;
dcheng84c358e2016-04-26 07:05:53153 std::unique_ptr<FlagsState> flags_state_;
jkrcal1383d1d2016-06-17 12:40:56154 base::FieldTrialList trial_list_;
sdefresne0e566342015-11-24 08:55:46155};
156
157TEST_F(FlagsStateTest, NoChangeNoRestart) {
158 EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
159 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, false);
160 EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
161
162 // kFlags6 is enabled by default, so enabling should not require a restart.
163 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags6, true);
164 EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
165}
166
167TEST_F(FlagsStateTest, ChangeNeedsRestart) {
168 EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
169 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
170 EXPECT_TRUE(flags_state_->IsRestartNeededToCommitChanges());
171}
172
173// Tests that disabling a default enabled entry requires a restart.
174TEST_F(FlagsStateTest, DisableChangeNeedsRestart) {
175 EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
176 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags6, false);
177 EXPECT_TRUE(flags_state_->IsRestartNeededToCommitChanges());
178}
179
180TEST_F(FlagsStateTest, MultiFlagChangeNeedsRestart) {
181 const FeatureEntry& entry = kEntries[3];
182 ASSERT_EQ(kFlags4, entry.internal_name);
183 EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
184 // Enable the 2nd choice of the multi-value.
jkrcal1383d1d2016-06-17 12:40:56185 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
sdefresne0e566342015-11-24 08:55:46186 true);
187 EXPECT_TRUE(flags_state_->IsRestartNeededToCommitChanges());
188 flags_state_->Reset();
189 EXPECT_FALSE(flags_state_->IsRestartNeededToCommitChanges());
190 // Enable the default choice now.
jkrcal1383d1d2016-06-17 12:40:56191 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
sdefresne0e566342015-11-24 08:55:46192 true);
193 EXPECT_TRUE(flags_state_->IsRestartNeededToCommitChanges());
194}
195
196TEST_F(FlagsStateTest, AddTwoFlagsRemoveOne) {
197 // Add two entries, check they're there.
198 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
199 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, true);
200
201 const base::ListValue* entries_list =
202 prefs_.GetList(prefs::kEnabledLabsExperiments);
203 ASSERT_TRUE(entries_list != nullptr);
204
205 ASSERT_EQ(2u, entries_list->GetSize());
206
207 std::string s0;
208 ASSERT_TRUE(entries_list->GetString(0, &s0));
209 std::string s1;
210 ASSERT_TRUE(entries_list->GetString(1, &s1));
211
212 EXPECT_TRUE(s0 == kFlags1 || s1 == kFlags1);
213 EXPECT_TRUE(s0 == kFlags2 || s1 == kFlags2);
214
215 // Remove one entry, check the other's still around.
216 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, false);
217
218 entries_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
219 ASSERT_TRUE(entries_list != nullptr);
220 ASSERT_EQ(1u, entries_list->GetSize());
221 ASSERT_TRUE(entries_list->GetString(0, &s0));
222 EXPECT_TRUE(s0 == kFlags1);
223}
224
225TEST_F(FlagsStateTest, AddTwoFlagsRemoveBoth) {
226 // Add two entries, check the pref exists.
227 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
228 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, true);
229 const base::ListValue* entries_list =
230 prefs_.GetList(prefs::kEnabledLabsExperiments);
231 ASSERT_TRUE(entries_list != nullptr);
232
233 // Remove both, the pref should have been removed completely.
234 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, false);
235 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, false);
236 entries_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
237 EXPECT_TRUE(entries_list == nullptr || entries_list->GetSize() == 0);
238}
239
240TEST_F(FlagsStateTest, ConvertFlagsToSwitches) {
241 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
242
243 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
244 command_line.AppendSwitch("foo");
245
246 EXPECT_TRUE(command_line.HasSwitch("foo"));
247 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
248
249 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
250 kAddSentinels, kEnableFeatures,
251 kDisableFeatures);
252
253 EXPECT_TRUE(command_line.HasSwitch("foo"));
254 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
255 EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesBegin));
256 EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesEnd));
257
258 base::CommandLine command_line2(base::CommandLine::NO_PROGRAM);
259
260 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line2,
261 kNoSentinels, kEnableFeatures,
262 kDisableFeatures);
263
264 EXPECT_TRUE(command_line2.HasSwitch(kSwitch1));
265 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesBegin));
266 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesEnd));
267}
268
jkrcal1383d1d2016-06-17 12:40:56269TEST_F(FlagsStateTest, RegisterAllFeatureVariationParameters) {
270 const FeatureEntry& entry = kEntries[7];
jkrcald1d20082016-07-14 15:04:24271 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
272
jkrcal05f7ce32016-07-13 16:42:09273 // Select the "Default" variation.
jkrcal1383d1d2016-06-17 12:40:56274 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
275 true);
jkrcald1d20082016-07-14 15:04:24276 flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
277 feature_list.get());
jkrcal1383d1d2016-06-17 12:40:56278 // No value should be associated.
279 EXPECT_EQ("", variations::GetVariationParamValue(kTestTrial, kTestParam));
280 // The trial should not be created.
281 base::FieldTrial* trial = base::FieldTrialList::Find(kTestTrial);
282 EXPECT_EQ(nullptr, trial);
283
jkrcal05f7ce32016-07-13 16:42:09284 // Select the default "Enabled" variation.
jkrcal1383d1d2016-06-17 12:40:56285 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(1),
286 true);
287
jkrcald1d20082016-07-14 15:04:24288 flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
289 feature_list.get());
jkrcal05f7ce32016-07-13 16:42:09290 // No value should be associated as this is the default option.
291 EXPECT_EQ("",
jkrcal1383d1d2016-06-17 12:40:56292 variations::GetVariationParamValue(kTestTrial, kTestParam));
293
294 // The trial should be created.
295 trial = base::FieldTrialList::Find(kTestTrial);
296 EXPECT_NE(nullptr, trial);
297 // The about:flags group should be selected for the trial.
298 EXPECT_EQ(internal::kTrialGroupAboutFlags, trial->group_name());
299
jkrcal05f7ce32016-07-13 16:42:09300 // Select the only one variation.
jkrcal1383d1d2016-06-17 12:40:56301 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
302 true);
jkrcald1d20082016-07-14 15:04:24303 flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
304 feature_list.get());
jkrcal1383d1d2016-06-17 12:40:56305 // Associating for the second time should not change the value.
jkrcal05f7ce32016-07-13 16:42:09306 EXPECT_EQ("",
307 variations::GetVariationParamValue(kTestTrial, kTestParam));
308}
309
310TEST_F(FlagsStateTest, RegisterAllFeatureVariationParametersNonDefault) {
311 const FeatureEntry& entry = kEntries[7];
jkrcald1d20082016-07-14 15:04:24312 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
313
jkrcal05f7ce32016-07-13 16:42:09314 // Select the only one variation.
315 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
316 true);
jkrcald1d20082016-07-14 15:04:24317 flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_,
318 feature_list.get());
319
320 // Set the feature_list as the main instance so that
321 // variations::GetVariationParamValueByFeature below works.
asvitkine9499b8d2016-08-09 05:37:07322 base::test::ScopedFeatureList scoped_feature_list;
323 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
jkrcald1d20082016-07-14 15:04:24324
jkrcal05f7ce32016-07-13 16:42:09325 // The param should have the value predefined in this variation.
326 EXPECT_EQ(kTestParamValue,
jkrcal1383d1d2016-06-17 12:40:56327 variations::GetVariationParamValue(kTestTrial, kTestParam));
jkrcald1d20082016-07-14 15:04:24328
329 // The value should be associated also via the name of the feature.
330 EXPECT_EQ(kTestParamValue, variations::GetVariationParamValueByFeature(
331 kTestFeature2, kTestParam));
jkrcal1383d1d2016-06-17 12:40:56332}
333
sdefresne0e566342015-11-24 08:55:46334base::CommandLine::StringType CreateSwitch(const std::string& value) {
335#if defined(OS_WIN)
336 return base::ASCIIToUTF16(value);
337#else
338 return value;
339#endif
340}
341
342TEST_F(FlagsStateTest, CompareSwitchesToCurrentCommandLine) {
343 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
344
345 const std::string kDoubleDash("--");
346
347 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
348 command_line.AppendSwitch("foo");
349
350 base::CommandLine new_command_line(base::CommandLine::NO_PROGRAM);
351 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &new_command_line,
352 kAddSentinels, kEnableFeatures,
353 kDisableFeatures);
354
355 EXPECT_FALSE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
356 new_command_line, command_line, nullptr, nullptr, nullptr));
357 {
358 std::set<base::CommandLine::StringType> difference;
359 EXPECT_FALSE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
360 new_command_line, command_line, &difference, nullptr, nullptr));
361 EXPECT_EQ(1U, difference.size());
362 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch1)));
363 }
364
365 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
366 kAddSentinels, kEnableFeatures,
367 kDisableFeatures);
368
369 EXPECT_TRUE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
370 new_command_line, command_line, nullptr, nullptr, nullptr));
371 {
372 std::set<base::CommandLine::StringType> difference;
373 EXPECT_TRUE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
374 new_command_line, command_line, &difference, nullptr, nullptr));
375 EXPECT_TRUE(difference.empty());
376 }
377
378 // Now both have flags but different.
379 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, false);
380 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, true);
381
382 base::CommandLine another_command_line(base::CommandLine::NO_PROGRAM);
383 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &another_command_line,
384 kAddSentinels, kEnableFeatures,
385 kDisableFeatures);
386
387 EXPECT_FALSE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
388 new_command_line, another_command_line, nullptr, nullptr, nullptr));
389 {
390 std::set<base::CommandLine::StringType> difference;
391 EXPECT_FALSE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
392 new_command_line, another_command_line, &difference, nullptr, nullptr));
393 EXPECT_EQ(2U, difference.size());
394 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch1)));
395 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch2 + "=" +
396 kValueForSwitch2)));
397 }
398}
399
400TEST_F(FlagsStateTest, RemoveFlagSwitches) {
401 std::map<std::string, base::CommandLine::StringType> switch_list;
402 switch_list[kSwitch1] = base::CommandLine::StringType();
403 switch_list[switches::kFlagSwitchesBegin] = base::CommandLine::StringType();
404 switch_list[switches::kFlagSwitchesEnd] = base::CommandLine::StringType();
405 switch_list["foo"] = base::CommandLine::StringType();
406
407 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
408
409 // This shouldn't do anything before ConvertFlagsToSwitches() wasn't called.
410 flags_state_->RemoveFlagsSwitches(&switch_list);
411 ASSERT_EQ(4u, switch_list.size());
skyostil46384412016-08-12 14:33:39412 EXPECT_TRUE(base::ContainsKey(switch_list, kSwitch1));
413 EXPECT_TRUE(base::ContainsKey(switch_list, switches::kFlagSwitchesBegin));
414 EXPECT_TRUE(base::ContainsKey(switch_list, switches::kFlagSwitchesEnd));
415 EXPECT_TRUE(base::ContainsKey(switch_list, "foo"));
sdefresne0e566342015-11-24 08:55:46416
417 // Call ConvertFlagsToSwitches(), then RemoveFlagsSwitches() again.
418 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
419 command_line.AppendSwitch("foo");
420 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
421 kAddSentinels, kEnableFeatures,
422 kDisableFeatures);
423 flags_state_->RemoveFlagsSwitches(&switch_list);
424
425 // Now the about:flags-related switch should have been removed.
426 ASSERT_EQ(1u, switch_list.size());
skyostil46384412016-08-12 14:33:39427 EXPECT_TRUE(base::ContainsKey(switch_list, "foo"));
sdefresne0e566342015-11-24 08:55:46428}
429
430TEST_F(FlagsStateTest, RemoveFlagSwitches_Features) {
431 struct {
432 int enabled_choice; // 0: default, 1: enabled, 2: disabled.
433 const char* existing_enable_features;
434 const char* existing_disable_features;
435 const char* expected_enable_features;
436 const char* expected_disable_features;
437 } cases[] = {
438 // Default value: Should not affect existing flags.
439 {0, nullptr, nullptr, nullptr, nullptr},
440 {0, "A,B", "C", "A,B", "C"},
441 // "Enable" option: should only affect enabled list.
jkrcal1383d1d2016-06-17 12:40:56442 {1, nullptr, nullptr, "FeatureName1", nullptr},
443 {1, "A,B", "C", "A,B,FeatureName1", "C"},
sdefresne0e566342015-11-24 08:55:46444 // "Disable" option: should only affect disabled list.
jkrcal1383d1d2016-06-17 12:40:56445 {2, nullptr, nullptr, nullptr, "FeatureName1"},
446 {2, "A,B", "C", "A,B", "C,FeatureName1"},
sdefresne0e566342015-11-24 08:55:46447 };
448
449 for (size_t i = 0; i < arraysize(cases); ++i) {
450 SCOPED_TRACE(base::StringPrintf(
451 "Test[%" PRIuS "]: %d [%s] [%s]", i, cases[i].enabled_choice,
452 cases[i].existing_enable_features ? cases[i].existing_enable_features
453 : "null",
454 cases[i].existing_disable_features ? cases[i].existing_disable_features
455 : "null"));
456
457 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
458 if (cases[i].existing_enable_features) {
459 command_line.AppendSwitchASCII(kEnableFeatures,
460 cases[i].existing_enable_features);
461 }
462 if (cases[i].existing_disable_features) {
463 command_line.AppendSwitchASCII(kDisableFeatures,
464 cases[i].existing_disable_features);
465 }
466
467 flags_state_->Reset();
468
469 const std::string entry_name = base::StringPrintf(
470 "%s%s%d", kFlags7, testing::kMultiSeparator, cases[i].enabled_choice);
471 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry_name, true);
472
473 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
474 kAddSentinels, kEnableFeatures,
475 kDisableFeatures);
476 auto switch_list = command_line.GetSwitches();
477 EXPECT_EQ(cases[i].expected_enable_features != nullptr,
skyostil46384412016-08-12 14:33:39478 base::ContainsKey(switch_list, kEnableFeatures));
sdefresne0e566342015-11-24 08:55:46479 if (cases[i].expected_enable_features)
480 EXPECT_EQ(CreateSwitch(cases[i].expected_enable_features),
481 switch_list[kEnableFeatures]);
482
483 EXPECT_EQ(cases[i].expected_disable_features != nullptr,
skyostil46384412016-08-12 14:33:39484 base::ContainsKey(switch_list, kDisableFeatures));
sdefresne0e566342015-11-24 08:55:46485 if (cases[i].expected_disable_features)
486 EXPECT_EQ(CreateSwitch(cases[i].expected_disable_features),
487 switch_list[kDisableFeatures]);
488
489 // RemoveFlagsSwitches() should result in the original values for these
490 // switches.
491 switch_list = command_line.GetSwitches();
492 flags_state_->RemoveFlagsSwitches(&switch_list);
493 EXPECT_EQ(cases[i].existing_enable_features != nullptr,
skyostil46384412016-08-12 14:33:39494 base::ContainsKey(switch_list, kEnableFeatures));
sdefresne0e566342015-11-24 08:55:46495 if (cases[i].existing_enable_features)
496 EXPECT_EQ(CreateSwitch(cases[i].existing_enable_features),
497 switch_list[kEnableFeatures]);
498 EXPECT_EQ(cases[i].existing_disable_features != nullptr,
skyostil46384412016-08-12 14:33:39499 base::ContainsKey(switch_list, kEnableFeatures));
sdefresne0e566342015-11-24 08:55:46500 if (cases[i].existing_disable_features)
501 EXPECT_EQ(CreateSwitch(cases[i].existing_disable_features),
502 switch_list[kDisableFeatures]);
503 }
504}
505
506// Tests enabling entries that aren't supported on the current platform.
507TEST_F(FlagsStateTest, PersistAndPrune) {
508 // Enable entries 1 and 3.
509 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
510 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags3, true);
511 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
512 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
513 EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
514
515 // Convert the flags to switches. Entry 3 shouldn't be among the switches
516 // as it is not applicable to the current platform.
517 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
518 kAddSentinels, kEnableFeatures,
519 kDisableFeatures);
520 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
521 EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
522
523 // FeatureEntry 3 should show still be persisted in preferences though.
524 const base::ListValue* entries_list =
525 prefs_.GetList(prefs::kEnabledLabsExperiments);
526 ASSERT_TRUE(entries_list);
527 EXPECT_EQ(2U, entries_list->GetSize());
528 std::string s0;
529 ASSERT_TRUE(entries_list->GetString(0, &s0));
530 EXPECT_EQ(kFlags1, s0);
531 std::string s1;
532 ASSERT_TRUE(entries_list->GetString(1, &s1));
533 EXPECT_EQ(kFlags3, s1);
534}
535
536// Tests that switches which should have values get them in the command
537// line.
538TEST_F(FlagsStateTest, CheckValues) {
539 // Enable entries 1 and 2.
540 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
541 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, true);
542 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
543 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
544 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
545
546 // Convert the flags to switches.
547 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
548 kAddSentinels, kEnableFeatures,
549 kDisableFeatures);
550 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
551 EXPECT_EQ(std::string(), command_line.GetSwitchValueASCII(kSwitch1));
552 EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
553 EXPECT_EQ(std::string(kValueForSwitch2),
554 command_line.GetSwitchValueASCII(kSwitch2));
555
556 // Confirm that there is no '=' in the command line for simple switches.
557 std::string switch1_with_equals =
558 std::string("--") + std::string(kSwitch1) + std::string("=");
559#if defined(OS_WIN)
560 EXPECT_EQ(base::string16::npos, command_line.GetCommandLineString().find(
561 base::ASCIIToUTF16(switch1_with_equals)));
562#else
563 EXPECT_EQ(std::string::npos,
564 command_line.GetCommandLineString().find(switch1_with_equals));
565#endif
566
567 // And confirm there is a '=' for switches with values.
568 std::string switch2_with_equals =
569 std::string("--") + std::string(kSwitch2) + std::string("=");
570#if defined(OS_WIN)
571 EXPECT_NE(base::string16::npos, command_line.GetCommandLineString().find(
572 base::ASCIIToUTF16(switch2_with_equals)));
573#else
574 EXPECT_NE(std::string::npos,
575 command_line.GetCommandLineString().find(switch2_with_equals));
576#endif
577
578 // And it should persist.
579 const base::ListValue* entries_list =
580 prefs_.GetList(prefs::kEnabledLabsExperiments);
581 ASSERT_TRUE(entries_list);
582 EXPECT_EQ(2U, entries_list->GetSize());
583 std::string s0;
584 ASSERT_TRUE(entries_list->GetString(0, &s0));
585 EXPECT_EQ(kFlags1, s0);
586 std::string s1;
587 ASSERT_TRUE(entries_list->GetString(1, &s1));
588 EXPECT_EQ(kFlags2, s1);
589}
590
591// Tests multi-value type entries.
592TEST_F(FlagsStateTest, MultiValues) {
593 const FeatureEntry& entry = kEntries[3];
594 ASSERT_EQ(kFlags4, entry.internal_name);
595
596 // Initially, the first "deactivated" option of the multi entry should
597 // be set.
598 {
599 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
600 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
601 kAddSentinels, kEnableFeatures,
602 kDisableFeatures);
603 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
604 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
605 }
606
607 // Enable the 2nd choice of the multi-value.
jkrcal1383d1d2016-06-17 12:40:56608 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
sdefresne0e566342015-11-24 08:55:46609 true);
610 {
611 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
612 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
613 kAddSentinels, kEnableFeatures,
614 kDisableFeatures);
615 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
616 EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch2));
617 EXPECT_EQ(std::string(kValueForMultiSwitch2),
618 command_line.GetSwitchValueASCII(kMultiSwitch2));
619 }
620
621 // Disable the multi-value entry.
jkrcal1383d1d2016-06-17 12:40:56622 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
sdefresne0e566342015-11-24 08:55:46623 true);
624 {
625 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
626 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
627 kAddSentinels, kEnableFeatures,
628 kDisableFeatures);
629 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
630 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
631 }
632}
633
634// Tests that disable flags are added when an entry is disabled.
635TEST_F(FlagsStateTest, DisableFlagCommandLine) {
636 // Nothing selected.
637 {
638 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
639 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
640 kAddSentinels, kEnableFeatures,
641 kDisableFeatures);
642 EXPECT_FALSE(command_line.HasSwitch(kSwitch6));
643 }
644
645 // Disable the entry 6.
646 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags6, false);
647 {
648 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
649 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
650 kAddSentinels, kEnableFeatures,
651 kDisableFeatures);
652 EXPECT_TRUE(command_line.HasSwitch(kSwitch6));
653 }
654
655 // Enable entry 6.
656 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags6, true);
657 {
658 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
659 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
660 kAddSentinels, kEnableFeatures,
661 kDisableFeatures);
662 EXPECT_FALSE(command_line.HasSwitch(kSwitch6));
663 }
664}
665
666TEST_F(FlagsStateTest, EnableDisableValues) {
667 const FeatureEntry& entry = kEntries[4];
668 ASSERT_EQ(kFlags5, entry.internal_name);
669
670 // Nothing selected.
671 {
672 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
673 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
674 kAddSentinels, kEnableFeatures,
675 kDisableFeatures);
676 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
677 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
678 }
679
680 // "Enable" option selected.
jkrcal1383d1d2016-06-17 12:40:56681 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(1),
sdefresne0e566342015-11-24 08:55:46682 true);
683 {
684 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
685 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
686 kAddSentinels, kEnableFeatures,
687 kDisableFeatures);
688 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
689 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
690 EXPECT_EQ(kEnableDisableValue1, command_line.GetSwitchValueASCII(kSwitch1));
691 }
692
693 // "Disable" option selected.
jkrcal1383d1d2016-06-17 12:40:56694 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
sdefresne0e566342015-11-24 08:55:46695 true);
696 {
697 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
698 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
699 kAddSentinels, kEnableFeatures,
700 kDisableFeatures);
701 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
702 EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
703 EXPECT_EQ(kEnableDisableValue2, command_line.GetSwitchValueASCII(kSwitch2));
704 }
705
706 // "Default" option selected, same as nothing selected.
jkrcal1383d1d2016-06-17 12:40:56707 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
sdefresne0e566342015-11-24 08:55:46708 true);
709 {
710 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
711 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
712 kAddSentinels, kEnableFeatures,
713 kDisableFeatures);
714 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
715 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
716 }
717}
718
719TEST_F(FlagsStateTest, FeatureValues) {
720 const FeatureEntry& entry = kEntries[6];
721 ASSERT_EQ(kFlags7, entry.internal_name);
722
723 struct {
724 int enabled_choice;
725 const char* existing_enable_features;
726 const char* existing_disable_features;
727 const char* expected_enable_features;
728 const char* expected_disable_features;
729 } cases[] = {
730 // Nothing selected.
731 {-1, nullptr, nullptr, "", ""},
732 // "Default" option selected, same as nothing selected.
733 {0, nullptr, nullptr, "", ""},
734 // "Enable" option selected.
jkrcal1383d1d2016-06-17 12:40:56735 {1, nullptr, nullptr, "FeatureName1", ""},
sdefresne0e566342015-11-24 08:55:46736 // "Disable" option selected.
jkrcal1383d1d2016-06-17 12:40:56737 {2, nullptr, nullptr, "", "FeatureName1"},
sdefresne0e566342015-11-24 08:55:46738 // "Enable" option should get added to the existing list.
jkrcal1383d1d2016-06-17 12:40:56739 {1, "Foo,Bar", nullptr, "Foo,Bar,FeatureName1", ""},
sdefresne0e566342015-11-24 08:55:46740 // "Disable" option should get added to the existing list.
jkrcal1383d1d2016-06-17 12:40:56741 {2, nullptr, "Foo,Bar", "", "Foo,Bar,FeatureName1"},
sdefresne0e566342015-11-24 08:55:46742 };
743
744 for (size_t i = 0; i < arraysize(cases); ++i) {
745 SCOPED_TRACE(base::StringPrintf(
746 "Test[%" PRIuS "]: %d [%s] [%s]", i, cases[i].enabled_choice,
747 cases[i].existing_enable_features ? cases[i].existing_enable_features
748 : "null",
749 cases[i].existing_disable_features ? cases[i].existing_disable_features
750 : "null"));
751
752 if (cases[i].enabled_choice != -1) {
753 flags_state_->SetFeatureEntryEnabled(
jkrcal1383d1d2016-06-17 12:40:56754 &flags_storage_, entry.NameForOption(cases[i].enabled_choice), true);
sdefresne0e566342015-11-24 08:55:46755 }
756
757 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
758 if (cases[i].existing_enable_features) {
759 command_line.AppendSwitchASCII(kEnableFeatures,
760 cases[i].existing_enable_features);
761 }
762 if (cases[i].existing_disable_features) {
763 command_line.AppendSwitchASCII(kDisableFeatures,
764 cases[i].existing_disable_features);
765 }
766
767 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
768 kAddSentinels, kEnableFeatures,
769 kDisableFeatures);
770 EXPECT_EQ(cases[i].expected_enable_features,
771 command_line.GetSwitchValueASCII(kEnableFeatures));
772 EXPECT_EQ(cases[i].expected_disable_features,
773 command_line.GetSwitchValueASCII(kDisableFeatures));
774 }
775}
776
777TEST_F(FlagsStateTest, GetFlagFeatureEntries) {
778 base::ListValue supported_entries;
779 base::ListValue unsupported_entries;
780 flags_state_->GetFlagFeatureEntries(&flags_storage_, kGeneralAccessFlagsOnly,
781 &supported_entries, &unsupported_entries,
782 base::Bind(&SkipFeatureEntry));
783 // All |kEntries| except for |kFlags3| should be supported.
jkrcal1383d1d2016-06-17 12:40:56784 EXPECT_EQ(7u, supported_entries.GetSize());
sdefresne0e566342015-11-24 08:55:46785 EXPECT_EQ(1u, unsupported_entries.GetSize());
786 EXPECT_EQ(arraysize(kEntries),
787 supported_entries.GetSize() + unsupported_entries.GetSize());
788}
789
790} // namespace flags_ui