blob: b95373a5f4ba8775df577b6b104c9dd971de43c7 [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"
22#include "base/values.h"
avibc5337b2015-12-25 23:16:3323#include "build/build_config.h"
sdefresne0e566342015-11-24 08:55:4624#include "components/flags_ui/feature_entry.h"
25#include "components/flags_ui/flags_ui_pref_names.h"
26#include "components/flags_ui/flags_ui_switches.h"
27#include "components/flags_ui/pref_service_flags_storage.h"
brettw066508682016-02-03 08:22:0228#include "components/prefs/pref_registry_simple.h"
29#include "components/prefs/testing_pref_service.h"
jkrcal1383d1d2016-06-17 12:40:5630#include "components/variations/variations_associated_data.h"
sdefresne0e566342015-11-24 08:55:4631#include "grit/components_strings.h"
32#include "testing/gtest/include/gtest/gtest.h"
33
34namespace flags_ui {
35
36namespace {
37
38const char kFlags1[] = "flag1";
39const char kFlags2[] = "flag2";
40const char kFlags3[] = "flag3";
41const char kFlags4[] = "flag4";
42const char kFlags5[] = "flag5";
43const char kFlags6[] = "flag6";
44const char kFlags7[] = "flag7";
jkrcal1383d1d2016-06-17 12:40:5645const char kFlags8[] = "flag8";
sdefresne0e566342015-11-24 08:55:4646
47const char kSwitch1[] = "switch";
48const char kSwitch2[] = "switch2";
49const char kSwitch3[] = "switch3";
50const char kSwitch6[] = "switch6";
51const char kValueForSwitch2[] = "value_for_switch2";
52
53const char kMultiSwitch1[] = "multi_switch1";
54const char kMultiSwitch2[] = "multi_switch2";
55const char kValueForMultiSwitch2[] = "value_for_multi_switch2";
56
57const char kEnableDisableValue1[] = "value1";
58const char kEnableDisableValue2[] = "value2";
59
60const char kEnableFeatures[] = "dummy-enable-features";
61const char kDisableFeatures[] = "dummy-disable-features";
62
jkrcal1383d1d2016-06-17 12:40:5663const char kTestTrial[] = "TestTrial";
64const char kTestParam[] = "param";
65const char kTestParamValue1[] = "value1";
66const char kTestParamValue2[] = "value2";
67
68const base::Feature kTestFeature1{"FeatureName1",
69 base::FEATURE_ENABLED_BY_DEFAULT};
70const base::Feature kTestFeature2{"FeatureName2",
71 base::FEATURE_ENABLED_BY_DEFAULT};
72
73const FeatureEntry::FeatureParam kTestVariationDefault[] = {
74 {kTestParam, kTestParamValue1}};
75
76const FeatureEntry::FeatureParam kTestVariationOther[] = {
77 {kTestParam, kTestParamValue2}};
78
79const FeatureEntry::FeatureVariation kTestVariations[] = {
80 {"", kTestVariationDefault, 1},
81 {"dummy description", kTestVariationOther, 1}};
82
sdefresne0e566342015-11-24 08:55:4683// Those have to be valid ids for the translation system but the value are
84// never used, so pick one at random from the current component.
85const int kDummyNameId = IDS_FLAGS_UI_WARNING_HEADER;
86const int kDummyDescriptionId = IDS_FLAGS_UI_WARNING_TEXT;
87
88bool SkipFeatureEntry(const FeatureEntry& feature_entry) {
89 return false;
90}
91
92} // namespace
93
94const FeatureEntry::Choice kMultiChoices[] = {
95 {kDummyDescriptionId, "", ""},
96 {kDummyDescriptionId, kMultiSwitch1, ""},
97 {kDummyDescriptionId, kMultiSwitch2, kValueForMultiSwitch2},
98};
99
sdefresne0e566342015-11-24 08:55:46100// The entries that are set for these tests. The 3rd entry is not supported on
101// the current platform, all others are.
102static FeatureEntry kEntries[] = {
103 {kFlags1, kDummyNameId, kDummyDescriptionId,
104 0, // Ends up being mapped to the current platform.
jkrcal1383d1d2016-06-17 12:40:56105 FeatureEntry::SINGLE_VALUE, kSwitch1, "", nullptr, nullptr, nullptr, 0,
106 nullptr, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46107 {kFlags2, kDummyNameId, kDummyDescriptionId,
108 0, // Ends up being mapped to the current platform.
109 FeatureEntry::SINGLE_VALUE, kSwitch2, kValueForSwitch2, nullptr, nullptr,
jkrcal1383d1d2016-06-17 12:40:56110 nullptr, 0, nullptr, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46111 {kFlags3, kDummyNameId, kDummyDescriptionId,
112 0, // This ends up enabling for an OS other than the current.
jkrcal1383d1d2016-06-17 12:40:56113 FeatureEntry::SINGLE_VALUE, kSwitch3, "", nullptr, nullptr, nullptr, 0,
114 nullptr, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46115 {kFlags4, kDummyNameId, kDummyDescriptionId,
116 0, // Ends up being mapped to the current platform.
jkrcal1383d1d2016-06-17 12:40:56117 FeatureEntry::MULTI_VALUE, "", "", "", "", nullptr,
118 arraysize(kMultiChoices), kMultiChoices, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46119 {kFlags5, kDummyNameId, kDummyDescriptionId,
120 0, // Ends up being mapped to the current platform.
121 FeatureEntry::ENABLE_DISABLE_VALUE, kSwitch1, kEnableDisableValue1,
jkrcal1383d1d2016-06-17 12:40:56122 kSwitch2, kEnableDisableValue2, nullptr, 3, nullptr, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46123 {kFlags6, kDummyNameId, kDummyDescriptionId, 0,
124 FeatureEntry::SINGLE_DISABLE_VALUE, kSwitch6, "", nullptr, nullptr,
jkrcal1383d1d2016-06-17 12:40:56125 nullptr, 0, nullptr, nullptr, nullptr},
sdefresne0e566342015-11-24 08:55:46126 {kFlags7, kDummyNameId, kDummyDescriptionId,
127 0, // Ends up being mapped to the current platform.
128 FeatureEntry::FEATURE_VALUE, nullptr, nullptr, nullptr, nullptr,
jkrcal1383d1d2016-06-17 12:40:56129 &kTestFeature1, 3, nullptr, nullptr, nullptr},
130 {kFlags8, kDummyNameId, kDummyDescriptionId,
131 0, // Ends up being mapped to the current platform.
132 FeatureEntry::FEATURE_WITH_VARIATIONS_VALUE, nullptr, nullptr, nullptr,
133 nullptr, &kTestFeature2, 4, nullptr, kTestVariations, kTestTrial},
sdefresne0e566342015-11-24 08:55:46134};
135
136class FlagsStateTest : public ::testing::Test {
137 protected:
jkrcal1383d1d2016-06-17 12:40:56138 FlagsStateTest() : flags_storage_(&prefs_), trial_list_(nullptr) {
sdefresne0e566342015-11-24 08:55:46139 prefs_.registry()->RegisterListPref(prefs::kEnabledLabsExperiments);
140
141 for (size_t i = 0; i < arraysize(kEntries); ++i)
142 kEntries[i].supported_platforms = FlagsState::GetCurrentPlatform();
143
144 int os_other_than_current = 1;
145 while (os_other_than_current == FlagsState::GetCurrentPlatform())
146 os_other_than_current <<= 1;
147 kEntries[2].supported_platforms = os_other_than_current;
148 flags_state_.reset(new FlagsState(kEntries, arraysize(kEntries)));
149 }
150
151 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];
271 // Select the "Disabled" variation.
272 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
273 true);
274 flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_);
275 // No value should be associated.
276 EXPECT_EQ("", variations::GetVariationParamValue(kTestTrial, kTestParam));
277 // The trial should not be created.
278 base::FieldTrial* trial = base::FieldTrialList::Find(kTestTrial);
279 EXPECT_EQ(nullptr, trial);
280
281 // Select the first "Enabled" variation.
282 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(1),
283 true);
284
285 flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_);
286 // The value should be associated.
287 EXPECT_EQ(kTestParamValue1,
288 variations::GetVariationParamValue(kTestTrial, kTestParam));
289
290 // The trial should be created.
291 trial = base::FieldTrialList::Find(kTestTrial);
292 EXPECT_NE(nullptr, trial);
293 // The about:flags group should be selected for the trial.
294 EXPECT_EQ(internal::kTrialGroupAboutFlags, trial->group_name());
295
296 // Select the second "Enabled" variation.
297 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
298 true);
299 flags_state_->RegisterAllFeatureVariationParameters(&flags_storage_);
300 // Associating for the second time should not change the value.
301 EXPECT_EQ(kTestParamValue1,
302 variations::GetVariationParamValue(kTestTrial, kTestParam));
303}
304
sdefresne0e566342015-11-24 08:55:46305base::CommandLine::StringType CreateSwitch(const std::string& value) {
306#if defined(OS_WIN)
307 return base::ASCIIToUTF16(value);
308#else
309 return value;
310#endif
311}
312
313TEST_F(FlagsStateTest, CompareSwitchesToCurrentCommandLine) {
314 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
315
316 const std::string kDoubleDash("--");
317
318 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
319 command_line.AppendSwitch("foo");
320
321 base::CommandLine new_command_line(base::CommandLine::NO_PROGRAM);
322 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &new_command_line,
323 kAddSentinels, kEnableFeatures,
324 kDisableFeatures);
325
326 EXPECT_FALSE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
327 new_command_line, command_line, nullptr, nullptr, nullptr));
328 {
329 std::set<base::CommandLine::StringType> difference;
330 EXPECT_FALSE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
331 new_command_line, command_line, &difference, nullptr, nullptr));
332 EXPECT_EQ(1U, difference.size());
333 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch1)));
334 }
335
336 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
337 kAddSentinels, kEnableFeatures,
338 kDisableFeatures);
339
340 EXPECT_TRUE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
341 new_command_line, command_line, nullptr, nullptr, nullptr));
342 {
343 std::set<base::CommandLine::StringType> difference;
344 EXPECT_TRUE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
345 new_command_line, command_line, &difference, nullptr, nullptr));
346 EXPECT_TRUE(difference.empty());
347 }
348
349 // Now both have flags but different.
350 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, false);
351 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, true);
352
353 base::CommandLine another_command_line(base::CommandLine::NO_PROGRAM);
354 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &another_command_line,
355 kAddSentinels, kEnableFeatures,
356 kDisableFeatures);
357
358 EXPECT_FALSE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
359 new_command_line, another_command_line, nullptr, nullptr, nullptr));
360 {
361 std::set<base::CommandLine::StringType> difference;
362 EXPECT_FALSE(FlagsState::AreSwitchesIdenticalToCurrentCommandLine(
363 new_command_line, another_command_line, &difference, nullptr, nullptr));
364 EXPECT_EQ(2U, difference.size());
365 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch1)));
366 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch2 + "=" +
367 kValueForSwitch2)));
368 }
369}
370
371TEST_F(FlagsStateTest, RemoveFlagSwitches) {
372 std::map<std::string, base::CommandLine::StringType> switch_list;
373 switch_list[kSwitch1] = base::CommandLine::StringType();
374 switch_list[switches::kFlagSwitchesBegin] = base::CommandLine::StringType();
375 switch_list[switches::kFlagSwitchesEnd] = base::CommandLine::StringType();
376 switch_list["foo"] = base::CommandLine::StringType();
377
378 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
379
380 // This shouldn't do anything before ConvertFlagsToSwitches() wasn't called.
381 flags_state_->RemoveFlagsSwitches(&switch_list);
382 ASSERT_EQ(4u, switch_list.size());
383 EXPECT_TRUE(ContainsKey(switch_list, kSwitch1));
384 EXPECT_TRUE(ContainsKey(switch_list, switches::kFlagSwitchesBegin));
385 EXPECT_TRUE(ContainsKey(switch_list, switches::kFlagSwitchesEnd));
386 EXPECT_TRUE(ContainsKey(switch_list, "foo"));
387
388 // Call ConvertFlagsToSwitches(), then RemoveFlagsSwitches() again.
389 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
390 command_line.AppendSwitch("foo");
391 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
392 kAddSentinels, kEnableFeatures,
393 kDisableFeatures);
394 flags_state_->RemoveFlagsSwitches(&switch_list);
395
396 // Now the about:flags-related switch should have been removed.
397 ASSERT_EQ(1u, switch_list.size());
398 EXPECT_TRUE(ContainsKey(switch_list, "foo"));
399}
400
401TEST_F(FlagsStateTest, RemoveFlagSwitches_Features) {
402 struct {
403 int enabled_choice; // 0: default, 1: enabled, 2: disabled.
404 const char* existing_enable_features;
405 const char* existing_disable_features;
406 const char* expected_enable_features;
407 const char* expected_disable_features;
408 } cases[] = {
409 // Default value: Should not affect existing flags.
410 {0, nullptr, nullptr, nullptr, nullptr},
411 {0, "A,B", "C", "A,B", "C"},
412 // "Enable" option: should only affect enabled list.
jkrcal1383d1d2016-06-17 12:40:56413 {1, nullptr, nullptr, "FeatureName1", nullptr},
414 {1, "A,B", "C", "A,B,FeatureName1", "C"},
sdefresne0e566342015-11-24 08:55:46415 // "Disable" option: should only affect disabled list.
jkrcal1383d1d2016-06-17 12:40:56416 {2, nullptr, nullptr, nullptr, "FeatureName1"},
417 {2, "A,B", "C", "A,B", "C,FeatureName1"},
sdefresne0e566342015-11-24 08:55:46418 };
419
420 for (size_t i = 0; i < arraysize(cases); ++i) {
421 SCOPED_TRACE(base::StringPrintf(
422 "Test[%" PRIuS "]: %d [%s] [%s]", i, cases[i].enabled_choice,
423 cases[i].existing_enable_features ? cases[i].existing_enable_features
424 : "null",
425 cases[i].existing_disable_features ? cases[i].existing_disable_features
426 : "null"));
427
428 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
429 if (cases[i].existing_enable_features) {
430 command_line.AppendSwitchASCII(kEnableFeatures,
431 cases[i].existing_enable_features);
432 }
433 if (cases[i].existing_disable_features) {
434 command_line.AppendSwitchASCII(kDisableFeatures,
435 cases[i].existing_disable_features);
436 }
437
438 flags_state_->Reset();
439
440 const std::string entry_name = base::StringPrintf(
441 "%s%s%d", kFlags7, testing::kMultiSeparator, cases[i].enabled_choice);
442 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry_name, true);
443
444 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
445 kAddSentinels, kEnableFeatures,
446 kDisableFeatures);
447 auto switch_list = command_line.GetSwitches();
448 EXPECT_EQ(cases[i].expected_enable_features != nullptr,
449 ContainsKey(switch_list, kEnableFeatures));
450 if (cases[i].expected_enable_features)
451 EXPECT_EQ(CreateSwitch(cases[i].expected_enable_features),
452 switch_list[kEnableFeatures]);
453
454 EXPECT_EQ(cases[i].expected_disable_features != nullptr,
455 ContainsKey(switch_list, kDisableFeatures));
456 if (cases[i].expected_disable_features)
457 EXPECT_EQ(CreateSwitch(cases[i].expected_disable_features),
458 switch_list[kDisableFeatures]);
459
460 // RemoveFlagsSwitches() should result in the original values for these
461 // switches.
462 switch_list = command_line.GetSwitches();
463 flags_state_->RemoveFlagsSwitches(&switch_list);
464 EXPECT_EQ(cases[i].existing_enable_features != nullptr,
465 ContainsKey(switch_list, kEnableFeatures));
466 if (cases[i].existing_enable_features)
467 EXPECT_EQ(CreateSwitch(cases[i].existing_enable_features),
468 switch_list[kEnableFeatures]);
469 EXPECT_EQ(cases[i].existing_disable_features != nullptr,
470 ContainsKey(switch_list, kEnableFeatures));
471 if (cases[i].existing_disable_features)
472 EXPECT_EQ(CreateSwitch(cases[i].existing_disable_features),
473 switch_list[kDisableFeatures]);
474 }
475}
476
477// Tests enabling entries that aren't supported on the current platform.
478TEST_F(FlagsStateTest, PersistAndPrune) {
479 // Enable entries 1 and 3.
480 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
481 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags3, true);
482 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
483 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
484 EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
485
486 // Convert the flags to switches. Entry 3 shouldn't be among the switches
487 // as it is not applicable to the current platform.
488 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
489 kAddSentinels, kEnableFeatures,
490 kDisableFeatures);
491 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
492 EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
493
494 // FeatureEntry 3 should show still be persisted in preferences though.
495 const base::ListValue* entries_list =
496 prefs_.GetList(prefs::kEnabledLabsExperiments);
497 ASSERT_TRUE(entries_list);
498 EXPECT_EQ(2U, entries_list->GetSize());
499 std::string s0;
500 ASSERT_TRUE(entries_list->GetString(0, &s0));
501 EXPECT_EQ(kFlags1, s0);
502 std::string s1;
503 ASSERT_TRUE(entries_list->GetString(1, &s1));
504 EXPECT_EQ(kFlags3, s1);
505}
506
507// Tests that switches which should have values get them in the command
508// line.
509TEST_F(FlagsStateTest, CheckValues) {
510 // Enable entries 1 and 2.
511 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags1, true);
512 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags2, true);
513 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
514 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
515 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
516
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
527 // Confirm that there is no '=' in the command line for simple switches.
528 std::string switch1_with_equals =
529 std::string("--") + std::string(kSwitch1) + std::string("=");
530#if defined(OS_WIN)
531 EXPECT_EQ(base::string16::npos, command_line.GetCommandLineString().find(
532 base::ASCIIToUTF16(switch1_with_equals)));
533#else
534 EXPECT_EQ(std::string::npos,
535 command_line.GetCommandLineString().find(switch1_with_equals));
536#endif
537
538 // And confirm there is a '=' for switches with values.
539 std::string switch2_with_equals =
540 std::string("--") + std::string(kSwitch2) + std::string("=");
541#if defined(OS_WIN)
542 EXPECT_NE(base::string16::npos, command_line.GetCommandLineString().find(
543 base::ASCIIToUTF16(switch2_with_equals)));
544#else
545 EXPECT_NE(std::string::npos,
546 command_line.GetCommandLineString().find(switch2_with_equals));
547#endif
548
549 // And it should persist.
550 const base::ListValue* entries_list =
551 prefs_.GetList(prefs::kEnabledLabsExperiments);
552 ASSERT_TRUE(entries_list);
553 EXPECT_EQ(2U, entries_list->GetSize());
554 std::string s0;
555 ASSERT_TRUE(entries_list->GetString(0, &s0));
556 EXPECT_EQ(kFlags1, s0);
557 std::string s1;
558 ASSERT_TRUE(entries_list->GetString(1, &s1));
559 EXPECT_EQ(kFlags2, s1);
560}
561
562// Tests multi-value type entries.
563TEST_F(FlagsStateTest, MultiValues) {
564 const FeatureEntry& entry = kEntries[3];
565 ASSERT_EQ(kFlags4, entry.internal_name);
566
567 // Initially, the first "deactivated" option of the multi entry should
568 // be set.
569 {
570 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
571 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
572 kAddSentinels, kEnableFeatures,
573 kDisableFeatures);
574 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
575 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
576 }
577
578 // Enable the 2nd choice of the multi-value.
jkrcal1383d1d2016-06-17 12:40:56579 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
sdefresne0e566342015-11-24 08:55:46580 true);
581 {
582 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
583 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
584 kAddSentinels, kEnableFeatures,
585 kDisableFeatures);
586 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
587 EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch2));
588 EXPECT_EQ(std::string(kValueForMultiSwitch2),
589 command_line.GetSwitchValueASCII(kMultiSwitch2));
590 }
591
592 // Disable the multi-value entry.
jkrcal1383d1d2016-06-17 12:40:56593 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
sdefresne0e566342015-11-24 08:55:46594 true);
595 {
596 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
597 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
598 kAddSentinels, kEnableFeatures,
599 kDisableFeatures);
600 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
601 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
602 }
603}
604
605// Tests that disable flags are added when an entry is disabled.
606TEST_F(FlagsStateTest, DisableFlagCommandLine) {
607 // Nothing selected.
608 {
609 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
610 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
611 kAddSentinels, kEnableFeatures,
612 kDisableFeatures);
613 EXPECT_FALSE(command_line.HasSwitch(kSwitch6));
614 }
615
616 // Disable the entry 6.
617 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags6, false);
618 {
619 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
620 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
621 kAddSentinels, kEnableFeatures,
622 kDisableFeatures);
623 EXPECT_TRUE(command_line.HasSwitch(kSwitch6));
624 }
625
626 // Enable entry 6.
627 flags_state_->SetFeatureEntryEnabled(&flags_storage_, kFlags6, true);
628 {
629 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
630 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
631 kAddSentinels, kEnableFeatures,
632 kDisableFeatures);
633 EXPECT_FALSE(command_line.HasSwitch(kSwitch6));
634 }
635}
636
637TEST_F(FlagsStateTest, EnableDisableValues) {
638 const FeatureEntry& entry = kEntries[4];
639 ASSERT_EQ(kFlags5, entry.internal_name);
640
641 // Nothing selected.
642 {
643 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
644 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
645 kAddSentinels, kEnableFeatures,
646 kDisableFeatures);
647 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
648 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
649 }
650
651 // "Enable" option selected.
jkrcal1383d1d2016-06-17 12:40:56652 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(1),
sdefresne0e566342015-11-24 08:55:46653 true);
654 {
655 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
656 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
657 kAddSentinels, kEnableFeatures,
658 kDisableFeatures);
659 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
660 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
661 EXPECT_EQ(kEnableDisableValue1, command_line.GetSwitchValueASCII(kSwitch1));
662 }
663
664 // "Disable" option selected.
jkrcal1383d1d2016-06-17 12:40:56665 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(2),
sdefresne0e566342015-11-24 08:55:46666 true);
667 {
668 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
669 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
670 kAddSentinels, kEnableFeatures,
671 kDisableFeatures);
672 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
673 EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
674 EXPECT_EQ(kEnableDisableValue2, command_line.GetSwitchValueASCII(kSwitch2));
675 }
676
677 // "Default" option selected, same as nothing selected.
jkrcal1383d1d2016-06-17 12:40:56678 flags_state_->SetFeatureEntryEnabled(&flags_storage_, entry.NameForOption(0),
sdefresne0e566342015-11-24 08:55:46679 true);
680 {
681 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
682 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
683 kAddSentinels, kEnableFeatures,
684 kDisableFeatures);
685 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
686 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
687 }
688}
689
690TEST_F(FlagsStateTest, FeatureValues) {
691 const FeatureEntry& entry = kEntries[6];
692 ASSERT_EQ(kFlags7, entry.internal_name);
693
694 struct {
695 int enabled_choice;
696 const char* existing_enable_features;
697 const char* existing_disable_features;
698 const char* expected_enable_features;
699 const char* expected_disable_features;
700 } cases[] = {
701 // Nothing selected.
702 {-1, nullptr, nullptr, "", ""},
703 // "Default" option selected, same as nothing selected.
704 {0, nullptr, nullptr, "", ""},
705 // "Enable" option selected.
jkrcal1383d1d2016-06-17 12:40:56706 {1, nullptr, nullptr, "FeatureName1", ""},
sdefresne0e566342015-11-24 08:55:46707 // "Disable" option selected.
jkrcal1383d1d2016-06-17 12:40:56708 {2, nullptr, nullptr, "", "FeatureName1"},
sdefresne0e566342015-11-24 08:55:46709 // "Enable" option should get added to the existing list.
jkrcal1383d1d2016-06-17 12:40:56710 {1, "Foo,Bar", nullptr, "Foo,Bar,FeatureName1", ""},
sdefresne0e566342015-11-24 08:55:46711 // "Disable" option should get added to the existing list.
jkrcal1383d1d2016-06-17 12:40:56712 {2, nullptr, "Foo,Bar", "", "Foo,Bar,FeatureName1"},
sdefresne0e566342015-11-24 08:55:46713 };
714
715 for (size_t i = 0; i < arraysize(cases); ++i) {
716 SCOPED_TRACE(base::StringPrintf(
717 "Test[%" PRIuS "]: %d [%s] [%s]", i, cases[i].enabled_choice,
718 cases[i].existing_enable_features ? cases[i].existing_enable_features
719 : "null",
720 cases[i].existing_disable_features ? cases[i].existing_disable_features
721 : "null"));
722
723 if (cases[i].enabled_choice != -1) {
724 flags_state_->SetFeatureEntryEnabled(
jkrcal1383d1d2016-06-17 12:40:56725 &flags_storage_, entry.NameForOption(cases[i].enabled_choice), true);
sdefresne0e566342015-11-24 08:55:46726 }
727
728 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
729 if (cases[i].existing_enable_features) {
730 command_line.AppendSwitchASCII(kEnableFeatures,
731 cases[i].existing_enable_features);
732 }
733 if (cases[i].existing_disable_features) {
734 command_line.AppendSwitchASCII(kDisableFeatures,
735 cases[i].existing_disable_features);
736 }
737
738 flags_state_->ConvertFlagsToSwitches(&flags_storage_, &command_line,
739 kAddSentinels, kEnableFeatures,
740 kDisableFeatures);
741 EXPECT_EQ(cases[i].expected_enable_features,
742 command_line.GetSwitchValueASCII(kEnableFeatures));
743 EXPECT_EQ(cases[i].expected_disable_features,
744 command_line.GetSwitchValueASCII(kDisableFeatures));
745 }
746}
747
748TEST_F(FlagsStateTest, GetFlagFeatureEntries) {
749 base::ListValue supported_entries;
750 base::ListValue unsupported_entries;
751 flags_state_->GetFlagFeatureEntries(&flags_storage_, kGeneralAccessFlagsOnly,
752 &supported_entries, &unsupported_entries,
753 base::Bind(&SkipFeatureEntry));
754 // All |kEntries| except for |kFlags3| should be supported.
jkrcal1383d1d2016-06-17 12:40:56755 EXPECT_EQ(7u, supported_entries.GetSize());
sdefresne0e566342015-11-24 08:55:46756 EXPECT_EQ(1u, unsupported_entries.GetSize());
757 EXPECT_EQ(arraysize(kEntries),
758 supported_entries.GetSize() + unsupported_entries.GetSize());
759}
760
761} // namespace flags_ui