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