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