about_flags_unittest.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2011 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 "base/prefs/pref_registry_simple.h"
6#include "base/prefs/testing_pref_service.h"
7#include "base/strings/string_number_conversions.h"
8#include "base/strings/utf_string_conversions.h"
9#include "base/values.h"
10#include "chrome/browser/about_flags.h"
11#include "chrome/common/chrome_switches.h"
12#include "chrome/common/pref_names.h"
13#include "grit/chromium_strings.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16const char kFlags1[] = "flag1";
17const char kFlags2[] = "flag2";
18const char kFlags3[] = "flag3";
19const char kFlags4[] = "flag4";
20const char kFlags5[] = "flag5";
21
22const char kSwitch1[] = "switch";
23const char kSwitch2[] = "switch2";
24const char kSwitch3[] = "switch3";
25const char kValueForSwitch2[] = "value_for_switch2";
26
27const char kMultiSwitch1[] = "multi_switch1";
28const char kMultiSwitch2[] = "multi_switch2";
29const char kValueForMultiSwitch2[] = "value_for_multi_switch2";
30
31const char kEnableDisableValue1[] = "value1";
32const char kEnableDisableValue2[] = "value2";
33
34namespace about_flags {
35
36const Experiment::Choice kMultiChoices[] = {
37  { IDS_PRODUCT_NAME, "", "" },
38  { IDS_PRODUCT_NAME, kMultiSwitch1, "" },
39  { IDS_PRODUCT_NAME, kMultiSwitch2, kValueForMultiSwitch2 },
40};
41
42// The experiments that are set for these tests. The 3rd experiment is not
43// supported on the current platform, all others are.
44static Experiment kExperiments[] = {
45  {
46    kFlags1,
47    IDS_PRODUCT_NAME,
48    IDS_PRODUCT_NAME,
49    0,  // Ends up being mapped to the current platform.
50    Experiment::SINGLE_VALUE,
51    kSwitch1,
52    "",
53    NULL,
54    NULL,
55    NULL,
56    0
57  },
58  {
59    kFlags2,
60    IDS_PRODUCT_NAME,
61    IDS_PRODUCT_NAME,
62    0,  // Ends up being mapped to the current platform.
63    Experiment::SINGLE_VALUE,
64    kSwitch2,
65    kValueForSwitch2,
66    NULL,
67    NULL,
68    NULL,
69    0
70  },
71  {
72    kFlags3,
73    IDS_PRODUCT_NAME,
74    IDS_PRODUCT_NAME,
75    0,  // This ends up enabling for an OS other than the current.
76    Experiment::SINGLE_VALUE,
77    kSwitch3,
78    "",
79    NULL,
80    NULL,
81    NULL,
82    0
83  },
84  {
85    kFlags4,
86    IDS_PRODUCT_NAME,
87    IDS_PRODUCT_NAME,
88    0,  // Ends up being mapped to the current platform.
89    Experiment::MULTI_VALUE,
90    "",
91    "",
92    "",
93    "",
94    kMultiChoices,
95    arraysize(kMultiChoices)
96  },
97  {
98    kFlags5,
99    IDS_PRODUCT_NAME,
100    IDS_PRODUCT_NAME,
101    0,  // Ends up being mapped to the current platform.
102    Experiment::ENABLE_DISABLE_VALUE,
103    kSwitch1,
104    kEnableDisableValue1,
105    kSwitch2,
106    kEnableDisableValue2,
107    NULL,
108    3
109  },
110};
111
112class AboutFlagsTest : public ::testing::Test {
113 protected:
114  AboutFlagsTest() {
115    prefs_.registry()->RegisterListPref(prefs::kEnabledLabsExperiments);
116    testing::ClearState();
117  }
118
119  virtual void SetUp() OVERRIDE {
120    for (size_t i = 0; i < arraysize(kExperiments); ++i)
121      kExperiments[i].supported_platforms = GetCurrentPlatform();
122
123    int os_other_than_current = 1;
124    while (os_other_than_current == GetCurrentPlatform())
125      os_other_than_current <<= 1;
126    kExperiments[2].supported_platforms = os_other_than_current;
127
128    testing::SetExperiments(kExperiments, arraysize(kExperiments));
129  }
130
131  virtual void TearDown() OVERRIDE {
132    testing::SetExperiments(NULL, 0);
133  }
134
135  TestingPrefServiceSimple prefs_;
136};
137
138
139TEST_F(AboutFlagsTest, NoChangeNoRestart) {
140  EXPECT_FALSE(IsRestartNeededToCommitChanges());
141  SetExperimentEnabled(&prefs_, kFlags1, false);
142  EXPECT_FALSE(IsRestartNeededToCommitChanges());
143}
144
145TEST_F(AboutFlagsTest, ChangeNeedsRestart) {
146  EXPECT_FALSE(IsRestartNeededToCommitChanges());
147  SetExperimentEnabled(&prefs_, kFlags1, true);
148  EXPECT_TRUE(IsRestartNeededToCommitChanges());
149}
150
151TEST_F(AboutFlagsTest, MultiFlagChangeNeedsRestart) {
152  const Experiment& experiment = kExperiments[3];
153  ASSERT_EQ(kFlags4, experiment.internal_name);
154  EXPECT_FALSE(IsRestartNeededToCommitChanges());
155  // Enable the 2nd choice of the multi-value.
156  SetExperimentEnabled(&prefs_, experiment.NameForChoice(2), true);
157  EXPECT_TRUE(IsRestartNeededToCommitChanges());
158  testing::ClearState();
159  EXPECT_FALSE(IsRestartNeededToCommitChanges());
160  // Enable the default choice now.
161  SetExperimentEnabled(&prefs_, experiment.NameForChoice(0), true);
162  EXPECT_TRUE(IsRestartNeededToCommitChanges());
163}
164
165TEST_F(AboutFlagsTest, AddTwoFlagsRemoveOne) {
166  // Add two experiments, check they're there.
167  SetExperimentEnabled(&prefs_, kFlags1, true);
168  SetExperimentEnabled(&prefs_, kFlags2, true);
169
170  const ListValue* experiments_list = prefs_.GetList(
171      prefs::kEnabledLabsExperiments);
172  ASSERT_TRUE(experiments_list != NULL);
173
174  ASSERT_EQ(2u, experiments_list->GetSize());
175
176  std::string s0;
177  ASSERT_TRUE(experiments_list->GetString(0, &s0));
178  std::string s1;
179  ASSERT_TRUE(experiments_list->GetString(1, &s1));
180
181  EXPECT_TRUE(s0 == kFlags1 || s1 == kFlags1);
182  EXPECT_TRUE(s0 == kFlags2 || s1 == kFlags2);
183
184  // Remove one experiment, check the other's still around.
185  SetExperimentEnabled(&prefs_, kFlags2, false);
186
187  experiments_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
188  ASSERT_TRUE(experiments_list != NULL);
189  ASSERT_EQ(1u, experiments_list->GetSize());
190  ASSERT_TRUE(experiments_list->GetString(0, &s0));
191  EXPECT_TRUE(s0 == kFlags1);
192}
193
194TEST_F(AboutFlagsTest, AddTwoFlagsRemoveBoth) {
195  // Add two experiments, check the pref exists.
196  SetExperimentEnabled(&prefs_, kFlags1, true);
197  SetExperimentEnabled(&prefs_, kFlags2, true);
198  const ListValue* experiments_list = prefs_.GetList(
199      prefs::kEnabledLabsExperiments);
200  ASSERT_TRUE(experiments_list != NULL);
201
202  // Remove both, the pref should have been removed completely.
203  SetExperimentEnabled(&prefs_, kFlags1, false);
204  SetExperimentEnabled(&prefs_, kFlags2, false);
205  experiments_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
206  EXPECT_TRUE(experiments_list == NULL || experiments_list->GetSize() == 0);
207}
208
209TEST_F(AboutFlagsTest, ConvertFlagsToSwitches) {
210  SetExperimentEnabled(&prefs_, kFlags1, true);
211
212  CommandLine command_line(CommandLine::NO_PROGRAM);
213  command_line.AppendSwitch("foo");
214
215  EXPECT_TRUE(command_line.HasSwitch("foo"));
216  EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
217
218  ConvertFlagsToSwitches(&prefs_, &command_line);
219
220  EXPECT_TRUE(command_line.HasSwitch("foo"));
221  EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
222}
223
224TEST_F(AboutFlagsTest, RemoveFlagSwitches) {
225  std::map<std::string, CommandLine::StringType> switch_list;
226  switch_list[kSwitch1] = CommandLine::StringType();
227  switch_list[switches::kFlagSwitchesBegin] = CommandLine::StringType();
228  switch_list[switches::kFlagSwitchesEnd] = CommandLine::StringType();
229  switch_list["foo"] = CommandLine::StringType();
230
231  SetExperimentEnabled(&prefs_, kFlags1, true);
232
233  // This shouldn't do anything before ConvertFlagsToSwitches() wasn't called.
234  RemoveFlagsSwitches(&switch_list);
235  ASSERT_EQ(4u, switch_list.size());
236  EXPECT_TRUE(switch_list.find(kSwitch1) != switch_list.end());
237  EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesBegin) !=
238              switch_list.end());
239  EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesEnd) !=
240              switch_list.end());
241  EXPECT_TRUE(switch_list.find("foo") != switch_list.end());
242
243  // Call ConvertFlagsToSwitches(), then RemoveFlagsSwitches() again.
244  CommandLine command_line(CommandLine::NO_PROGRAM);
245  command_line.AppendSwitch("foo");
246  ConvertFlagsToSwitches(&prefs_, &command_line);
247  RemoveFlagsSwitches(&switch_list);
248
249  // Now the about:flags-related switch should have been removed.
250  ASSERT_EQ(1u, switch_list.size());
251  EXPECT_TRUE(switch_list.find("foo") != switch_list.end());
252}
253
254// Tests enabling experiments that aren't supported on the current platform.
255TEST_F(AboutFlagsTest, PersistAndPrune) {
256  // Enable experiments 1 and 3.
257  SetExperimentEnabled(&prefs_, kFlags1, true);
258  SetExperimentEnabled(&prefs_, kFlags3, true);
259  CommandLine command_line(CommandLine::NO_PROGRAM);
260  EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
261  EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
262
263  // Convert the flags to switches. Experiment 3 shouldn't be among the switches
264  // as it is not applicable to the current platform.
265  ConvertFlagsToSwitches(&prefs_, &command_line);
266  EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
267  EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
268
269  // Experiment 3 should show still be persisted in preferences though.
270  const ListValue* experiments_list =
271      prefs_.GetList(prefs::kEnabledLabsExperiments);
272  ASSERT_TRUE(experiments_list);
273  EXPECT_EQ(2U, experiments_list->GetSize());
274  std::string s0;
275  ASSERT_TRUE(experiments_list->GetString(0, &s0));
276  EXPECT_EQ(kFlags1, s0);
277  std::string s1;
278  ASSERT_TRUE(experiments_list->GetString(1, &s1));
279  EXPECT_EQ(kFlags3, s1);
280}
281
282// Tests that switches which should have values get them in the command
283// line.
284TEST_F(AboutFlagsTest, CheckValues) {
285  // Enable experiments 1 and 2.
286  SetExperimentEnabled(&prefs_, kFlags1, true);
287  SetExperimentEnabled(&prefs_, kFlags2, true);
288  CommandLine command_line(CommandLine::NO_PROGRAM);
289  EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
290  EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
291
292  // Convert the flags to switches.
293  ConvertFlagsToSwitches(&prefs_, &command_line);
294  EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
295  EXPECT_EQ(std::string(), command_line.GetSwitchValueASCII(kSwitch1));
296  EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
297  EXPECT_EQ(std::string(kValueForSwitch2),
298            command_line.GetSwitchValueASCII(kSwitch2));
299
300  // Confirm that there is no '=' in the command line for simple switches.
301  std::string switch1_with_equals = std::string("--") +
302                                    std::string(kSwitch1) +
303                                    std::string("=");
304#if defined(OS_WIN)
305  EXPECT_EQ(std::wstring::npos,
306            command_line.GetCommandLineString().find(
307                ASCIIToWide(switch1_with_equals)));
308#else
309  EXPECT_EQ(std::string::npos,
310            command_line.GetCommandLineString().find(switch1_with_equals));
311#endif
312
313  // And confirm there is a '=' for switches with values.
314  std::string switch2_with_equals = std::string("--") +
315                                    std::string(kSwitch2) +
316                                    std::string("=");
317#if defined(OS_WIN)
318  EXPECT_NE(std::wstring::npos,
319            command_line.GetCommandLineString().find(
320                ASCIIToWide(switch2_with_equals)));
321#else
322  EXPECT_NE(std::string::npos,
323            command_line.GetCommandLineString().find(switch2_with_equals));
324#endif
325
326  // And it should persist.
327  const ListValue* experiments_list =
328      prefs_.GetList(prefs::kEnabledLabsExperiments);
329  ASSERT_TRUE(experiments_list);
330  EXPECT_EQ(2U, experiments_list->GetSize());
331  std::string s0;
332  ASSERT_TRUE(experiments_list->GetString(0, &s0));
333  EXPECT_EQ(kFlags1, s0);
334  std::string s1;
335  ASSERT_TRUE(experiments_list->GetString(1, &s1));
336  EXPECT_EQ(kFlags2, s1);
337}
338
339// Tests multi-value type experiments.
340TEST_F(AboutFlagsTest, MultiValues) {
341  const Experiment& experiment = kExperiments[3];
342  ASSERT_EQ(kFlags4, experiment.internal_name);
343
344  // Initially, the first "deactivated" option of the multi experiment should
345  // be set.
346  {
347    CommandLine command_line(CommandLine::NO_PROGRAM);
348    ConvertFlagsToSwitches(&prefs_, &command_line);
349    EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
350    EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
351  }
352
353  // Enable the 2nd choice of the multi-value.
354  SetExperimentEnabled(&prefs_, experiment.NameForChoice(2), true);
355  {
356    CommandLine command_line(CommandLine::NO_PROGRAM);
357    ConvertFlagsToSwitches(&prefs_, &command_line);
358    EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
359    EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch2));
360    EXPECT_EQ(std::string(kValueForMultiSwitch2),
361              command_line.GetSwitchValueASCII(kMultiSwitch2));
362  }
363
364  // Disable the multi-value experiment.
365  SetExperimentEnabled(&prefs_, experiment.NameForChoice(0), true);
366  {
367    CommandLine command_line(CommandLine::NO_PROGRAM);
368    ConvertFlagsToSwitches(&prefs_, &command_line);
369    EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
370    EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
371  }
372}
373
374TEST_F(AboutFlagsTest, EnableDisableValues) {
375  const Experiment& experiment = kExperiments[4];
376  ASSERT_EQ(kFlags5, experiment.internal_name);
377
378  // Nothing selected.
379  {
380    CommandLine command_line(CommandLine::NO_PROGRAM);
381    ConvertFlagsToSwitches(&prefs_, &command_line);
382    EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
383    EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
384  }
385
386  // "Enable" option selected.
387  SetExperimentEnabled(&prefs_, experiment.NameForChoice(1), true);
388  {
389    CommandLine command_line(CommandLine::NO_PROGRAM);
390    ConvertFlagsToSwitches(&prefs_, &command_line);
391    EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
392    EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
393    EXPECT_EQ(kEnableDisableValue1, command_line.GetSwitchValueASCII(kSwitch1));
394  }
395
396  // "Disable" option selected.
397  SetExperimentEnabled(&prefs_, experiment.NameForChoice(2), true);
398  {
399    CommandLine command_line(CommandLine::NO_PROGRAM);
400    ConvertFlagsToSwitches(&prefs_, &command_line);
401    EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
402    EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
403    EXPECT_EQ(kEnableDisableValue2, command_line.GetSwitchValueASCII(kSwitch2));
404  }
405
406  // "Default" option selected, same as nothing selected.
407  SetExperimentEnabled(&prefs_, experiment.NameForChoice(0), true);
408  {
409    CommandLine command_line(CommandLine::NO_PROGRAM);
410    ConvertFlagsToSwitches(&prefs_, &command_line);
411    EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
412    EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
413  }
414}
415
416// Makes sure there are no separators in any of the experiment names.
417TEST_F(AboutFlagsTest, NoSeparators) {
418  testing::SetExperiments(NULL, 0);
419  size_t count;
420  const Experiment* experiments = testing::GetExperiments(&count);
421    for (size_t i = 0; i < count; ++i) {
422    std::string name = experiments->internal_name;
423    EXPECT_EQ(std::string::npos, name.find(testing::kMultiSeparator)) << i;
424  }
425}
426
427}  // namespace about_flags
428