blob: 3d23deeb89e83f2da962f4ee0dac9806816d51f4 [file] [log] [blame]
[email protected]1bc78422011-03-31 08:41:381// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]e2ddbc92010-10-15 20:02:072// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]4b66a7d2014-08-15 17:13:315#include <stdint.h>
asvitkinebff63b22014-11-25 01:03:526#include <utility>
[email protected]4b66a7d2014-08-15 17:13:317
8#include "base/files/file_path.h"
9#include "base/memory/scoped_vector.h"
10#include "base/path_service.h"
[email protected]3853a4c2013-02-11 17:15:5711#include "base/prefs/pref_registry_simple.h"
[email protected]e5ba874f2013-02-14 17:20:1912#include "base/prefs/testing_pref_service.h"
[email protected]3ea1b182013-02-08 22:38:4113#include "base/strings/string_number_conversions.h"
[email protected]4b66a7d2014-08-15 17:13:3114#include "base/strings/stringprintf.h"
[email protected]135cb802013-06-09 16:44:2015#include "base/strings/utf_string_conversions.h"
[email protected]a314ee5a2010-10-26 21:23:2816#include "base/values.h"
[email protected]8a6ff28d2010-12-02 16:35:1917#include "chrome/browser/about_flags.h"
[email protected]e6d1c4f2013-06-12 17:37:4018#include "chrome/browser/pref_service_flags_storage.h"
[email protected]e2ddbc92010-10-15 20:02:0719#include "chrome/common/chrome_switches.h"
20#include "chrome/common/pref_names.h"
[email protected]af39f002014-08-22 10:18:1821#include "chrome/grit/chromium_strings.h"
[email protected]e2ddbc92010-10-15 20:02:0722#include "testing/gtest/include/gtest/gtest.h"
[email protected]4b66a7d2014-08-15 17:13:3123#include "third_party/libxml/chromium/libxml_utils.h"
24
25namespace {
[email protected]e2ddbc92010-10-15 20:02:0726
[email protected]a314ee5a2010-10-26 21:23:2827const char kFlags1[] = "flag1";
28const char kFlags2[] = "flag2";
29const char kFlags3[] = "flag3";
[email protected]8a6ff28d2010-12-02 16:35:1930const char kFlags4[] = "flag4";
[email protected]83e9fa702013-02-25 19:30:4431const char kFlags5[] = "flag5";
[email protected]e2ddbc92010-10-15 20:02:0732
[email protected]a314ee5a2010-10-26 21:23:2833const char kSwitch1[] = "switch";
34const char kSwitch2[] = "switch2";
35const char kSwitch3[] = "switch3";
[email protected]a82744532011-02-11 16:15:5336const char kValueForSwitch2[] = "value_for_switch2";
[email protected]e2ddbc92010-10-15 20:02:0737
[email protected]8a6ff28d2010-12-02 16:35:1938const char kMultiSwitch1[] = "multi_switch1";
39const char kMultiSwitch2[] = "multi_switch2";
[email protected]a82744532011-02-11 16:15:5340const char kValueForMultiSwitch2[] = "value_for_multi_switch2";
[email protected]8a6ff28d2010-12-02 16:35:1941
[email protected]83e9fa702013-02-25 19:30:4442const char kEnableDisableValue1[] = "value1";
43const char kEnableDisableValue2[] = "value2";
44
alemate0107c3272014-09-03 04:30:0445typedef base::HistogramBase::Sample Sample;
46typedef std::map<std::string, Sample> SwitchToIdMap;
[email protected]4b66a7d2014-08-15 17:13:3147
48// This is a helper function to the ReadEnumFromHistogramsXml().
49// Extracts single enum (with integer values) from histograms.xml.
50// Expects |reader| to point at given enum.
51// Returns map { value => label }.
52// Returns empty map on error.
alemate0107c3272014-09-03 04:30:0453std::map<Sample, std::string> ParseEnumFromHistogramsXml(
[email protected]4b66a7d2014-08-15 17:13:3154 const std::string& enum_name,
55 XmlReader* reader) {
56 int entries_index = -1;
57
alemate0107c3272014-09-03 04:30:0458 std::map<Sample, std::string> result;
[email protected]4b66a7d2014-08-15 17:13:3159 bool success = true;
60
61 while (true) {
62 const std::string node_name = reader->NodeName();
63 if (node_name == "enum" && reader->IsClosingElement())
64 break;
65
66 if (node_name == "int") {
67 ++entries_index;
68 std::string value_str;
69 std::string label;
70 const bool has_value = reader->NodeAttribute("value", &value_str);
71 const bool has_label = reader->NodeAttribute("label", &label);
72 if (!has_value) {
73 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
74 << entries_index << ", label='" << label
75 << "'): No 'value' attribute.";
76 success = false;
77 }
78 if (!has_label) {
79 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
80 << entries_index << ", value_str='" << value_str
81 << "'): No 'label' attribute.";
82 success = false;
83 }
84
alemate0107c3272014-09-03 04:30:0485 Sample value;
86 if (has_value && !base::StringToInt(value_str, &value)) {
[email protected]4b66a7d2014-08-15 17:13:3187 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
88 << entries_index << ", label='" << label
89 << "', value_str='" << value_str
90 << "'): 'value' attribute is not integer.";
91 success = false;
92 }
93 if (result.count(value)) {
94 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
95 << entries_index << ", label='" << label
96 << "', value_str='" << value_str
97 << "'): duplicate value '" << value_str
98 << "' found in enum. The previous one has label='"
99 << result[value] << "'.";
100 success = false;
101 }
102 if (success) {
103 result[value] = label;
104 }
105 }
106 // All enum entries are on the same level, so it is enough to iterate
107 // until possible.
108 reader->Next();
109 }
alemate0107c3272014-09-03 04:30:04110 return (success ? result : std::map<Sample, std::string>());
[email protected]4b66a7d2014-08-15 17:13:31111}
112
113// Find and read given enum (with integer values) from histograms.xml.
114// |enum_name| - enum name.
115// |histograms_xml| - must be loaded histograms.xml file.
116//
117// Returns map { value => label } so that:
118// <int value="9" label="enable-pinch-virtual-viewport"/>
119// becomes:
120// { 9 => "enable-pinch-virtual-viewport" }
121// Returns empty map on error.
alemate0107c3272014-09-03 04:30:04122std::map<Sample, std::string> ReadEnumFromHistogramsXml(
[email protected]4b66a7d2014-08-15 17:13:31123 const std::string& enum_name,
124 XmlReader* histograms_xml) {
alemate0107c3272014-09-03 04:30:04125 std::map<Sample, std::string> login_custom_flags;
[email protected]4b66a7d2014-08-15 17:13:31126
127 // Implement simple depth first search.
128 while (true) {
129 const std::string node_name = histograms_xml->NodeName();
130 if (node_name == "enum") {
131 std::string name;
132 if (histograms_xml->NodeAttribute("name", &name) && name == enum_name) {
133 if (!login_custom_flags.empty()) {
134 EXPECT_TRUE(login_custom_flags.empty())
135 << "Duplicate enum '" << enum_name << "' found in histograms.xml";
alemate0107c3272014-09-03 04:30:04136 return std::map<Sample, std::string>();
[email protected]4b66a7d2014-08-15 17:13:31137 }
138
139 const bool got_into_enum = histograms_xml->Read();
140 if (got_into_enum) {
141 login_custom_flags =
142 ParseEnumFromHistogramsXml(enum_name, histograms_xml);
143 EXPECT_FALSE(login_custom_flags.empty())
144 << "Bad enum '" << enum_name
145 << "' found in histograms.xml (format error).";
146 } else {
147 EXPECT_TRUE(got_into_enum)
148 << "Bad enum '" << enum_name
149 << "' (looks empty) found in histograms.xml.";
150 }
151 if (login_custom_flags.empty())
alemate0107c3272014-09-03 04:30:04152 return std::map<Sample, std::string>();
[email protected]4b66a7d2014-08-15 17:13:31153 }
154 }
155 // Go deeper if possible (stops at the closing tag of the deepest node).
156 if (histograms_xml->Read())
157 continue;
158
159 // Try next node on the same level (skips closing tag).
160 if (histograms_xml->Next())
161 continue;
162
163 // Go up until next node on the same level exists.
164 while (histograms_xml->Depth() && !histograms_xml->SkipToElement()) {
165 }
166
167 // Reached top. histograms.xml consists of the single top level node
168 // 'histogram-configuration', so this is the end.
169 if (!histograms_xml->Depth())
170 break;
171 }
172 EXPECT_FALSE(login_custom_flags.empty())
173 << "Enum '" << enum_name << "' is not found in histograms.xml.";
174 return login_custom_flags;
175}
176
177std::string FilePathStringTypeToString(const base::FilePath::StringType& path) {
178#if defined(OS_WIN)
179 return base::UTF16ToUTF8(path);
180#else
181 return path;
182#endif
183}
184
185std::set<std::string> GetAllSwitchesForTesting() {
186 std::set<std::string> result;
187
188 size_t num_experiments = 0;
189 const about_flags::Experiment* experiments =
190 about_flags::testing::GetExperiments(&num_experiments);
191
192 for (size_t i = 0; i < num_experiments; ++i) {
193 const about_flags::Experiment& experiment = experiments[i];
194 if (experiment.type == about_flags::Experiment::SINGLE_VALUE) {
195 result.insert(experiment.command_line_switch);
196 } else if (experiment.type == about_flags::Experiment::MULTI_VALUE) {
197 for (int j = 0; j < experiment.num_choices; ++j) {
198 result.insert(experiment.choices[j].command_line_switch);
199 }
200 } else {
201 DCHECK_EQ(experiment.type, about_flags::Experiment::ENABLE_DISABLE_VALUE);
202 result.insert(experiment.command_line_switch);
203 result.insert(experiment.disable_command_line_switch);
204 }
205 }
206 return result;
207}
208
209} // anonymous namespace
210
[email protected]e2ddbc92010-10-15 20:02:07211namespace about_flags {
212
[email protected]8a6ff28d2010-12-02 16:35:19213const Experiment::Choice kMultiChoices[] = {
[email protected]a82744532011-02-11 16:15:53214 { IDS_PRODUCT_NAME, "", "" },
215 { IDS_PRODUCT_NAME, kMultiSwitch1, "" },
216 { IDS_PRODUCT_NAME, kMultiSwitch2, kValueForMultiSwitch2 },
[email protected]8a6ff28d2010-12-02 16:35:19217};
218
219// The experiments that are set for these tests. The 3rd experiment is not
220// supported on the current platform, all others are.
[email protected]a314ee5a2010-10-26 21:23:28221static Experiment kExperiments[] = {
222 {
223 kFlags1,
224 IDS_PRODUCT_NAME,
225 IDS_PRODUCT_NAME,
226 0, // Ends up being mapped to the current platform.
[email protected]8a6ff28d2010-12-02 16:35:19227 Experiment::SINGLE_VALUE,
228 kSwitch1,
[email protected]a82744532011-02-11 16:15:53229 "",
[email protected]8a6ff28d2010-12-02 16:35:19230 NULL,
[email protected]83e9fa702013-02-25 19:30:44231 NULL,
232 NULL,
[email protected]8a6ff28d2010-12-02 16:35:19233 0
[email protected]a314ee5a2010-10-26 21:23:28234 },
235 {
236 kFlags2,
237 IDS_PRODUCT_NAME,
238 IDS_PRODUCT_NAME,
239 0, // Ends up being mapped to the current platform.
[email protected]8a6ff28d2010-12-02 16:35:19240 Experiment::SINGLE_VALUE,
241 kSwitch2,
[email protected]a82744532011-02-11 16:15:53242 kValueForSwitch2,
[email protected]8a6ff28d2010-12-02 16:35:19243 NULL,
[email protected]83e9fa702013-02-25 19:30:44244 NULL,
245 NULL,
[email protected]8a6ff28d2010-12-02 16:35:19246 0
[email protected]a314ee5a2010-10-26 21:23:28247 },
248 {
249 kFlags3,
250 IDS_PRODUCT_NAME,
251 IDS_PRODUCT_NAME,
252 0, // This ends up enabling for an OS other than the current.
[email protected]8a6ff28d2010-12-02 16:35:19253 Experiment::SINGLE_VALUE,
254 kSwitch3,
[email protected]a82744532011-02-11 16:15:53255 "",
[email protected]8a6ff28d2010-12-02 16:35:19256 NULL,
[email protected]83e9fa702013-02-25 19:30:44257 NULL,
258 NULL,
[email protected]8a6ff28d2010-12-02 16:35:19259 0
260 },
261 {
262 kFlags4,
263 IDS_PRODUCT_NAME,
264 IDS_PRODUCT_NAME,
265 0, // Ends up being mapped to the current platform.
266 Experiment::MULTI_VALUE,
267 "",
[email protected]a82744532011-02-11 16:15:53268 "",
[email protected]83e9fa702013-02-25 19:30:44269 "",
270 "",
[email protected]8a6ff28d2010-12-02 16:35:19271 kMultiChoices,
272 arraysize(kMultiChoices)
[email protected]a314ee5a2010-10-26 21:23:28273 },
[email protected]83e9fa702013-02-25 19:30:44274 {
275 kFlags5,
276 IDS_PRODUCT_NAME,
277 IDS_PRODUCT_NAME,
278 0, // Ends up being mapped to the current platform.
279 Experiment::ENABLE_DISABLE_VALUE,
280 kSwitch1,
281 kEnableDisableValue1,
282 kSwitch2,
283 kEnableDisableValue2,
284 NULL,
285 3
286 },
[email protected]a314ee5a2010-10-26 21:23:28287};
288
[email protected]e2ddbc92010-10-15 20:02:07289class AboutFlagsTest : public ::testing::Test {
290 protected:
[email protected]e6d1c4f2013-06-12 17:37:40291 AboutFlagsTest() : flags_storage_(&prefs_) {
[email protected]b1de2c72013-02-06 02:45:47292 prefs_.registry()->RegisterListPref(prefs::kEnabledLabsExperiments);
[email protected]e2ddbc92010-10-15 20:02:07293 testing::ClearState();
294 }
295
dchenge1bc7982014-10-30 00:32:40296 void SetUp() override {
[email protected]a314ee5a2010-10-26 21:23:28297 for (size_t i = 0; i < arraysize(kExperiments); ++i)
298 kExperiments[i].supported_platforms = GetCurrentPlatform();
299
300 int os_other_than_current = 1;
301 while (os_other_than_current == GetCurrentPlatform())
302 os_other_than_current <<= 1;
303 kExperiments[2].supported_platforms = os_other_than_current;
304
305 testing::SetExperiments(kExperiments, arraysize(kExperiments));
306 }
307
dchenge1bc7982014-10-30 00:32:40308 void TearDown() override { testing::SetExperiments(NULL, 0); }
[email protected]a314ee5a2010-10-26 21:23:28309
[email protected]5b199522012-12-22 17:24:44310 TestingPrefServiceSimple prefs_;
[email protected]e6d1c4f2013-06-12 17:37:40311 PrefServiceFlagsStorage flags_storage_;
[email protected]e2ddbc92010-10-15 20:02:07312};
313
[email protected]147492b2013-03-19 23:52:08314
315TEST_F(AboutFlagsTest, NoChangeNoRestart) {
316 EXPECT_FALSE(IsRestartNeededToCommitChanges());
[email protected]e6d1c4f2013-06-12 17:37:40317 SetExperimentEnabled(&flags_storage_, kFlags1, false);
[email protected]147492b2013-03-19 23:52:08318 EXPECT_FALSE(IsRestartNeededToCommitChanges());
319}
320
[email protected]e2ddbc92010-10-15 20:02:07321TEST_F(AboutFlagsTest, ChangeNeedsRestart) {
[email protected]e2ddbc92010-10-15 20:02:07322 EXPECT_FALSE(IsRestartNeededToCommitChanges());
[email protected]e6d1c4f2013-06-12 17:37:40323 SetExperimentEnabled(&flags_storage_, kFlags1, true);
[email protected]e2ddbc92010-10-15 20:02:07324 EXPECT_TRUE(IsRestartNeededToCommitChanges());
325}
326
[email protected]147492b2013-03-19 23:52:08327TEST_F(AboutFlagsTest, MultiFlagChangeNeedsRestart) {
328 const Experiment& experiment = kExperiments[3];
329 ASSERT_EQ(kFlags4, experiment.internal_name);
330 EXPECT_FALSE(IsRestartNeededToCommitChanges());
331 // Enable the 2nd choice of the multi-value.
[email protected]e6d1c4f2013-06-12 17:37:40332 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
[email protected]147492b2013-03-19 23:52:08333 EXPECT_TRUE(IsRestartNeededToCommitChanges());
334 testing::ClearState();
335 EXPECT_FALSE(IsRestartNeededToCommitChanges());
336 // Enable the default choice now.
[email protected]e6d1c4f2013-06-12 17:37:40337 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
[email protected]147492b2013-03-19 23:52:08338 EXPECT_TRUE(IsRestartNeededToCommitChanges());
339}
340
[email protected]e2ddbc92010-10-15 20:02:07341TEST_F(AboutFlagsTest, AddTwoFlagsRemoveOne) {
[email protected]e2ddbc92010-10-15 20:02:07342 // Add two experiments, check they're there.
[email protected]e6d1c4f2013-06-12 17:37:40343 SetExperimentEnabled(&flags_storage_, kFlags1, true);
344 SetExperimentEnabled(&flags_storage_, kFlags2, true);
[email protected]e2ddbc92010-10-15 20:02:07345
[email protected]5bcdd99d2013-12-23 18:28:30346 const base::ListValue* experiments_list = prefs_.GetList(
[email protected]e2ddbc92010-10-15 20:02:07347 prefs::kEnabledLabsExperiments);
348 ASSERT_TRUE(experiments_list != NULL);
349
350 ASSERT_EQ(2u, experiments_list->GetSize());
351
352 std::string s0;
353 ASSERT_TRUE(experiments_list->GetString(0, &s0));
354 std::string s1;
355 ASSERT_TRUE(experiments_list->GetString(1, &s1));
356
357 EXPECT_TRUE(s0 == kFlags1 || s1 == kFlags1);
358 EXPECT_TRUE(s0 == kFlags2 || s1 == kFlags2);
359
360 // Remove one experiment, check the other's still around.
[email protected]e6d1c4f2013-06-12 17:37:40361 SetExperimentEnabled(&flags_storage_, kFlags2, false);
[email protected]e2ddbc92010-10-15 20:02:07362
[email protected]1bc78422011-03-31 08:41:38363 experiments_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
[email protected]e2ddbc92010-10-15 20:02:07364 ASSERT_TRUE(experiments_list != NULL);
365 ASSERT_EQ(1u, experiments_list->GetSize());
366 ASSERT_TRUE(experiments_list->GetString(0, &s0));
367 EXPECT_TRUE(s0 == kFlags1);
368}
369
370TEST_F(AboutFlagsTest, AddTwoFlagsRemoveBoth) {
[email protected]e2ddbc92010-10-15 20:02:07371 // Add two experiments, check the pref exists.
[email protected]e6d1c4f2013-06-12 17:37:40372 SetExperimentEnabled(&flags_storage_, kFlags1, true);
373 SetExperimentEnabled(&flags_storage_, kFlags2, true);
[email protected]5bcdd99d2013-12-23 18:28:30374 const base::ListValue* experiments_list = prefs_.GetList(
[email protected]e2ddbc92010-10-15 20:02:07375 prefs::kEnabledLabsExperiments);
376 ASSERT_TRUE(experiments_list != NULL);
377
378 // Remove both, the pref should have been removed completely.
[email protected]e6d1c4f2013-06-12 17:37:40379 SetExperimentEnabled(&flags_storage_, kFlags1, false);
380 SetExperimentEnabled(&flags_storage_, kFlags2, false);
[email protected]1bc78422011-03-31 08:41:38381 experiments_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
[email protected]e2ddbc92010-10-15 20:02:07382 EXPECT_TRUE(experiments_list == NULL || experiments_list->GetSize() == 0);
383}
384
385TEST_F(AboutFlagsTest, ConvertFlagsToSwitches) {
[email protected]e6d1c4f2013-06-12 17:37:40386 SetExperimentEnabled(&flags_storage_, kFlags1, true);
[email protected]e2ddbc92010-10-15 20:02:07387
[email protected]947446b2010-10-21 03:36:31388 CommandLine command_line(CommandLine::NO_PROGRAM);
[email protected]e2ddbc92010-10-15 20:02:07389 command_line.AppendSwitch("foo");
390
391 EXPECT_TRUE(command_line.HasSwitch("foo"));
[email protected]a314ee5a2010-10-26 21:23:28392 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
[email protected]e2ddbc92010-10-15 20:02:07393
[email protected]578f2092013-09-16 17:16:21394 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]e2ddbc92010-10-15 20:02:07395
396 EXPECT_TRUE(command_line.HasSwitch("foo"));
[email protected]cd7fa99f2011-09-07 01:24:55397 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
[email protected]578f2092013-09-16 17:16:21398 EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesBegin));
399 EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesEnd));
400
401 CommandLine command_line2(CommandLine::NO_PROGRAM);
402
403 ConvertFlagsToSwitches(&flags_storage_, &command_line2, kNoSentinels);
404
405 EXPECT_TRUE(command_line2.HasSwitch(kSwitch1));
406 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesBegin));
407 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesEnd));
[email protected]e2ddbc92010-10-15 20:02:07408}
409
[email protected]4b66a7d2014-08-15 17:13:31410CommandLine::StringType CreateSwitch(const std::string& value) {
411#if defined(OS_WIN)
412 return base::ASCIIToUTF16(value);
413#else
414 return value;
415#endif
416}
417
[email protected]6d98abf2013-06-17 23:35:51418TEST_F(AboutFlagsTest, CompareSwitchesToCurrentCommandLine) {
419 SetExperimentEnabled(&flags_storage_, kFlags1, true);
420
[email protected]4b66a7d2014-08-15 17:13:31421 const std::string kDoubleDash("--");
422
[email protected]6d98abf2013-06-17 23:35:51423 CommandLine command_line(CommandLine::NO_PROGRAM);
424 command_line.AppendSwitch("foo");
425
426 CommandLine new_command_line(CommandLine::NO_PROGRAM);
[email protected]578f2092013-09-16 17:16:21427 ConvertFlagsToSwitches(&flags_storage_, &new_command_line, kAddSentinels);
[email protected]6d98abf2013-06-17 23:35:51428
[email protected]4b66a7d2014-08-15 17:13:31429 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
430 new_command_line, command_line, NULL));
431 {
432 std::set<CommandLine::StringType> difference;
433 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
434 new_command_line, command_line, &difference));
435 EXPECT_EQ(1U, difference.size());
436 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch1)));
437 }
[email protected]6d98abf2013-06-17 23:35:51438
[email protected]578f2092013-09-16 17:16:21439 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]6d98abf2013-06-17 23:35:51440
[email protected]4b66a7d2014-08-15 17:13:31441 EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(
442 new_command_line, command_line, NULL));
443 {
444 std::set<CommandLine::StringType> difference;
445 EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(
446 new_command_line, command_line, &difference));
447 EXPECT_TRUE(difference.empty());
448 }
[email protected]6d98abf2013-06-17 23:35:51449
450 // Now both have flags but different.
451 SetExperimentEnabled(&flags_storage_, kFlags1, false);
452 SetExperimentEnabled(&flags_storage_, kFlags2, true);
453
454 CommandLine another_command_line(CommandLine::NO_PROGRAM);
[email protected]578f2092013-09-16 17:16:21455 ConvertFlagsToSwitches(&flags_storage_, &another_command_line, kAddSentinels);
[email protected]6d98abf2013-06-17 23:35:51456
[email protected]4b66a7d2014-08-15 17:13:31457 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
458 new_command_line, another_command_line, NULL));
459 {
460 std::set<CommandLine::StringType> difference;
461 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
462 new_command_line, another_command_line, &difference));
463 EXPECT_EQ(2U, difference.size());
464 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch1)));
465 EXPECT_EQ(1U,
466 difference.count(CreateSwitch(kDoubleDash + kSwitch2 + "=" +
467 kValueForSwitch2)));
468 }
[email protected]6d98abf2013-06-17 23:35:51469}
470
[email protected]e2ddbc92010-10-15 20:02:07471TEST_F(AboutFlagsTest, RemoveFlagSwitches) {
[email protected]e2ddbc92010-10-15 20:02:07472 std::map<std::string, CommandLine::StringType> switch_list;
[email protected]a314ee5a2010-10-26 21:23:28473 switch_list[kSwitch1] = CommandLine::StringType();
[email protected]e2ddbc92010-10-15 20:02:07474 switch_list[switches::kFlagSwitchesBegin] = CommandLine::StringType();
475 switch_list[switches::kFlagSwitchesEnd] = CommandLine::StringType();
476 switch_list["foo"] = CommandLine::StringType();
477
[email protected]e6d1c4f2013-06-12 17:37:40478 SetExperimentEnabled(&flags_storage_, kFlags1, true);
[email protected]e2ddbc92010-10-15 20:02:07479
480 // This shouldn't do anything before ConvertFlagsToSwitches() wasn't called.
481 RemoveFlagsSwitches(&switch_list);
482 ASSERT_EQ(4u, switch_list.size());
[email protected]a314ee5a2010-10-26 21:23:28483 EXPECT_TRUE(switch_list.find(kSwitch1) != switch_list.end());
[email protected]e2ddbc92010-10-15 20:02:07484 EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesBegin) !=
485 switch_list.end());
486 EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesEnd) !=
487 switch_list.end());
488 EXPECT_TRUE(switch_list.find("foo") != switch_list.end());
489
490 // Call ConvertFlagsToSwitches(), then RemoveFlagsSwitches() again.
[email protected]947446b2010-10-21 03:36:31491 CommandLine command_line(CommandLine::NO_PROGRAM);
[email protected]e2ddbc92010-10-15 20:02:07492 command_line.AppendSwitch("foo");
[email protected]578f2092013-09-16 17:16:21493 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]e2ddbc92010-10-15 20:02:07494 RemoveFlagsSwitches(&switch_list);
495
496 // Now the about:flags-related switch should have been removed.
497 ASSERT_EQ(1u, switch_list.size());
498 EXPECT_TRUE(switch_list.find("foo") != switch_list.end());
499}
500
[email protected]a314ee5a2010-10-26 21:23:28501// Tests enabling experiments that aren't supported on the current platform.
502TEST_F(AboutFlagsTest, PersistAndPrune) {
[email protected]8a6ff28d2010-12-02 16:35:19503 // Enable experiments 1 and 3.
[email protected]e6d1c4f2013-06-12 17:37:40504 SetExperimentEnabled(&flags_storage_, kFlags1, true);
505 SetExperimentEnabled(&flags_storage_, kFlags3, true);
[email protected]a314ee5a2010-10-26 21:23:28506 CommandLine command_line(CommandLine::NO_PROGRAM);
507 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
508 EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
509
510 // Convert the flags to switches. Experiment 3 shouldn't be among the switches
511 // as it is not applicable to the current platform.
[email protected]578f2092013-09-16 17:16:21512 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]cd7fa99f2011-09-07 01:24:55513 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
[email protected]a314ee5a2010-10-26 21:23:28514 EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
515
516 // Experiment 3 should show still be persisted in preferences though.
[email protected]5bcdd99d2013-12-23 18:28:30517 const base::ListValue* experiments_list =
[email protected]ee28495a2013-05-20 04:10:52518 prefs_.GetList(prefs::kEnabledLabsExperiments);
519 ASSERT_TRUE(experiments_list);
520 EXPECT_EQ(2U, experiments_list->GetSize());
521 std::string s0;
522 ASSERT_TRUE(experiments_list->GetString(0, &s0));
523 EXPECT_EQ(kFlags1, s0);
524 std::string s1;
525 ASSERT_TRUE(experiments_list->GetString(1, &s1));
526 EXPECT_EQ(kFlags3, s1);
[email protected]8a6ff28d2010-12-02 16:35:19527}
528
[email protected]a82744532011-02-11 16:15:53529// Tests that switches which should have values get them in the command
530// line.
531TEST_F(AboutFlagsTest, CheckValues) {
532 // Enable experiments 1 and 2.
[email protected]e6d1c4f2013-06-12 17:37:40533 SetExperimentEnabled(&flags_storage_, kFlags1, true);
534 SetExperimentEnabled(&flags_storage_, kFlags2, true);
[email protected]a82744532011-02-11 16:15:53535 CommandLine command_line(CommandLine::NO_PROGRAM);
536 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
537 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
538
539 // Convert the flags to switches.
[email protected]578f2092013-09-16 17:16:21540 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]cd7fa99f2011-09-07 01:24:55541 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
[email protected]007b3f82013-04-09 08:46:45542 EXPECT_EQ(std::string(), command_line.GetSwitchValueASCII(kSwitch1));
[email protected]cd7fa99f2011-09-07 01:24:55543 EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
544 EXPECT_EQ(std::string(kValueForSwitch2),
[email protected]a82744532011-02-11 16:15:53545 command_line.GetSwitchValueASCII(kSwitch2));
546
547 // Confirm that there is no '=' in the command line for simple switches.
548 std::string switch1_with_equals = std::string("--") +
549 std::string(kSwitch1) +
550 std::string("=");
551#if defined(OS_WIN)
thestig11bf74d2014-11-24 20:14:42552 EXPECT_EQ(base::string16::npos,
[email protected]61a4c6f2011-07-20 04:54:52553 command_line.GetCommandLineString().find(
thestig11bf74d2014-11-24 20:14:42554 base::ASCIIToUTF16(switch1_with_equals)));
[email protected]a82744532011-02-11 16:15:53555#else
556 EXPECT_EQ(std::string::npos,
[email protected]61a4c6f2011-07-20 04:54:52557 command_line.GetCommandLineString().find(switch1_with_equals));
[email protected]a82744532011-02-11 16:15:53558#endif
559
560 // And confirm there is a '=' for switches with values.
561 std::string switch2_with_equals = std::string("--") +
562 std::string(kSwitch2) +
563 std::string("=");
[email protected]5b199522012-12-22 17:24:44564#if defined(OS_WIN)
thestig11bf74d2014-11-24 20:14:42565 EXPECT_NE(base::string16::npos,
[email protected]61a4c6f2011-07-20 04:54:52566 command_line.GetCommandLineString().find(
thestig11bf74d2014-11-24 20:14:42567 base::ASCIIToUTF16(switch2_with_equals)));
[email protected]a82744532011-02-11 16:15:53568#else
[email protected]cd7fa99f2011-09-07 01:24:55569 EXPECT_NE(std::string::npos,
[email protected]61a4c6f2011-07-20 04:54:52570 command_line.GetCommandLineString().find(switch2_with_equals));
[email protected]a82744532011-02-11 16:15:53571#endif
572
[email protected]ee28495a2013-05-20 04:10:52573 // And it should persist.
[email protected]5bcdd99d2013-12-23 18:28:30574 const base::ListValue* experiments_list =
[email protected]ee28495a2013-05-20 04:10:52575 prefs_.GetList(prefs::kEnabledLabsExperiments);
576 ASSERT_TRUE(experiments_list);
577 EXPECT_EQ(2U, experiments_list->GetSize());
578 std::string s0;
579 ASSERT_TRUE(experiments_list->GetString(0, &s0));
580 EXPECT_EQ(kFlags1, s0);
581 std::string s1;
582 ASSERT_TRUE(experiments_list->GetString(1, &s1));
583 EXPECT_EQ(kFlags2, s1);
[email protected]a82744532011-02-11 16:15:53584}
585
[email protected]28e35af2011-02-09 12:56:22586// Tests multi-value type experiments.
[email protected]8a6ff28d2010-12-02 16:35:19587TEST_F(AboutFlagsTest, MultiValues) {
[email protected]83e9fa702013-02-25 19:30:44588 const Experiment& experiment = kExperiments[3];
589 ASSERT_EQ(kFlags4, experiment.internal_name);
590
[email protected]28e35af2011-02-09 12:56:22591 // Initially, the first "deactivated" option of the multi experiment should
592 // be set.
[email protected]8a6ff28d2010-12-02 16:35:19593 {
594 CommandLine command_line(CommandLine::NO_PROGRAM);
[email protected]578f2092013-09-16 17:16:21595 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]28e35af2011-02-09 12:56:22596 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
[email protected]8a6ff28d2010-12-02 16:35:19597 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
598 }
599
[email protected]28e35af2011-02-09 12:56:22600 // Enable the 2nd choice of the multi-value.
[email protected]e6d1c4f2013-06-12 17:37:40601 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
[email protected]8a6ff28d2010-12-02 16:35:19602 {
603 CommandLine command_line(CommandLine::NO_PROGRAM);
[email protected]578f2092013-09-16 17:16:21604 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]8a6ff28d2010-12-02 16:35:19605 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
[email protected]cd7fa99f2011-09-07 01:24:55606 EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch2));
607 EXPECT_EQ(std::string(kValueForMultiSwitch2),
[email protected]a82744532011-02-11 16:15:53608 command_line.GetSwitchValueASCII(kMultiSwitch2));
[email protected]8a6ff28d2010-12-02 16:35:19609 }
610
611 // Disable the multi-value experiment.
[email protected]e6d1c4f2013-06-12 17:37:40612 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
[email protected]83e9fa702013-02-25 19:30:44613 {
614 CommandLine command_line(CommandLine::NO_PROGRAM);
[email protected]578f2092013-09-16 17:16:21615 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]83e9fa702013-02-25 19:30:44616 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
617 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
618 }
619}
620
621TEST_F(AboutFlagsTest, EnableDisableValues) {
622 const Experiment& experiment = kExperiments[4];
623 ASSERT_EQ(kFlags5, experiment.internal_name);
624
625 // Nothing selected.
626 {
627 CommandLine command_line(CommandLine::NO_PROGRAM);
[email protected]578f2092013-09-16 17:16:21628 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]83e9fa702013-02-25 19:30:44629 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
630 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
631 }
632
633 // "Enable" option selected.
[email protected]e6d1c4f2013-06-12 17:37:40634 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(1), true);
[email protected]83e9fa702013-02-25 19:30:44635 {
636 CommandLine command_line(CommandLine::NO_PROGRAM);
[email protected]578f2092013-09-16 17:16:21637 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]83e9fa702013-02-25 19:30:44638 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
639 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
640 EXPECT_EQ(kEnableDisableValue1, command_line.GetSwitchValueASCII(kSwitch1));
641 }
642
643 // "Disable" option selected.
[email protected]e6d1c4f2013-06-12 17:37:40644 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
[email protected]83e9fa702013-02-25 19:30:44645 {
646 CommandLine command_line(CommandLine::NO_PROGRAM);
[email protected]578f2092013-09-16 17:16:21647 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]83e9fa702013-02-25 19:30:44648 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
649 EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
650 EXPECT_EQ(kEnableDisableValue2, command_line.GetSwitchValueASCII(kSwitch2));
651 }
652
653 // "Default" option selected, same as nothing selected.
[email protected]e6d1c4f2013-06-12 17:37:40654 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
[email protected]8a6ff28d2010-12-02 16:35:19655 {
656 CommandLine command_line(CommandLine::NO_PROGRAM);
[email protected]578f2092013-09-16 17:16:21657 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
[email protected]8a6ff28d2010-12-02 16:35:19658 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
659 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
660 }
661}
662
663// Makes sure there are no separators in any of the experiment names.
664TEST_F(AboutFlagsTest, NoSeparators) {
665 testing::SetExperiments(NULL, 0);
666 size_t count;
667 const Experiment* experiments = testing::GetExperiments(&count);
668 for (size_t i = 0; i < count; ++i) {
669 std::string name = experiments->internal_name;
670 EXPECT_EQ(std::string::npos, name.find(testing::kMultiSeparator)) << i;
671 }
[email protected]a314ee5a2010-10-26 21:23:28672}
673
[email protected]4b66a7d2014-08-15 17:13:31674class AboutFlagsHistogramTest : public ::testing::Test {
675 protected:
676 // This is a helper function to check that all IDs in enum LoginCustomFlags in
677 // histograms.xml are unique.
678 void SetSwitchToHistogramIdMapping(const std::string& switch_name,
alemate0107c3272014-09-03 04:30:04679 const Sample switch_histogram_id,
680 std::map<std::string, Sample>* out_map) {
681 const std::pair<std::map<std::string, Sample>::iterator, bool> status =
[email protected]4b66a7d2014-08-15 17:13:31682 out_map->insert(std::make_pair(switch_name, switch_histogram_id));
683 if (!status.second) {
684 EXPECT_TRUE(status.first->second == switch_histogram_id)
685 << "Duplicate switch '" << switch_name
686 << "' found in enum 'LoginCustomFlags' in histograms.xml.";
687 }
688 }
689
690 // This method generates a hint for the user for what string should be added
691 // to the enum LoginCustomFlags to make in consistent.
692 std::string GetHistogramEnumEntryText(const std::string& switch_name,
alemate0107c3272014-09-03 04:30:04693 Sample value) {
[email protected]4b66a7d2014-08-15 17:13:31694 return base::StringPrintf(
alemate0107c3272014-09-03 04:30:04695 "<int value=\"%d\" label=\"%s\"/>", value, switch_name.c_str());
[email protected]4b66a7d2014-08-15 17:13:31696 }
697};
698
699TEST_F(AboutFlagsHistogramTest, CheckHistograms) {
700 base::FilePath histograms_xml_file_path;
701 ASSERT_TRUE(
702 PathService::Get(base::DIR_SOURCE_ROOT, &histograms_xml_file_path));
703 histograms_xml_file_path = histograms_xml_file_path.AppendASCII("tools")
704 .AppendASCII("metrics")
705 .AppendASCII("histograms")
706 .AppendASCII("histograms.xml");
707
708 XmlReader histograms_xml;
709 ASSERT_TRUE(histograms_xml.LoadFile(
710 FilePathStringTypeToString(histograms_xml_file_path.value())));
alemate0107c3272014-09-03 04:30:04711 std::map<Sample, std::string> login_custom_flags =
[email protected]4b66a7d2014-08-15 17:13:31712 ReadEnumFromHistogramsXml("LoginCustomFlags", &histograms_xml);
713 ASSERT_TRUE(login_custom_flags.size())
714 << "Error reading enum 'LoginCustomFlags' from histograms.xml.";
715
716 // Build reverse map {switch_name => id} from login_custom_flags.
717 SwitchToIdMap histograms_xml_switches_ids;
718
asvitkinebff63b22014-11-25 01:03:52719 EXPECT_TRUE(login_custom_flags.count(testing::kBadSwitchFormatHistogramId))
[email protected]4b66a7d2014-08-15 17:13:31720 << "Entry for UMA ID of incorrect command-line flag is not found in "
721 "histograms.xml enum LoginCustomFlags. "
722 "Consider adding entry:\n"
723 << " " << GetHistogramEnumEntryText("BAD_FLAG_FORMAT", 0);
724 // Check that all LoginCustomFlags entries have correct values.
asvitkinebff63b22014-11-25 01:03:52725 for (const auto& entry : login_custom_flags) {
726 if (entry.first == testing::kBadSwitchFormatHistogramId) {
727 // Add error value with empty name.
728 SetSwitchToHistogramIdMapping(std::string(), entry.first,
729 &histograms_xml_switches_ids);
[email protected]4b66a7d2014-08-15 17:13:31730 continue;
731 }
asvitkinebff63b22014-11-25 01:03:52732 const Sample uma_id = GetSwitchUMAId(entry.second);
733 EXPECT_EQ(uma_id, entry.first)
[email protected]4b66a7d2014-08-15 17:13:31734 << "histograms.xml enum LoginCustomFlags "
asvitkinebff63b22014-11-25 01:03:52735 "entry '" << entry.second << "' has incorrect value=" << entry.first
[email protected]4b66a7d2014-08-15 17:13:31736 << ", but " << uma_id << " is expected. Consider changing entry to:\n"
asvitkinebff63b22014-11-25 01:03:52737 << " " << GetHistogramEnumEntryText(entry.second, uma_id);
738 SetSwitchToHistogramIdMapping(entry.second, entry.first,
739 &histograms_xml_switches_ids);
[email protected]4b66a7d2014-08-15 17:13:31740 }
741
742 // Check that all flags in about_flags.cc have entries in login_custom_flags.
743 std::set<std::string> all_switches = GetAllSwitchesForTesting();
asvitkinebff63b22014-11-25 01:03:52744 for (const std::string& flag : all_switches) {
[email protected]4b66a7d2014-08-15 17:13:31745 // Skip empty placeholders.
asvitkinebff63b22014-11-25 01:03:52746 if (flag.empty())
[email protected]4b66a7d2014-08-15 17:13:31747 continue;
asvitkinebff63b22014-11-25 01:03:52748 const Sample uma_id = GetSwitchUMAId(flag);
749 EXPECT_NE(testing::kBadSwitchFormatHistogramId, uma_id)
750 << "Command-line switch '" << flag
[email protected]4b66a7d2014-08-15 17:13:31751 << "' from about_flags.cc has UMA ID equal to reserved value "
asvitkinebff63b22014-11-25 01:03:52752 "kBadSwitchFormatHistogramId="
753 << testing::kBadSwitchFormatHistogramId
[email protected]4b66a7d2014-08-15 17:13:31754 << ". Please modify switch name.";
755 SwitchToIdMap::iterator enum_entry =
asvitkinebff63b22014-11-25 01:03:52756 histograms_xml_switches_ids.lower_bound(flag);
[email protected]4b66a7d2014-08-15 17:13:31757
758 // Ignore case here when switch ID is incorrect - it has already been
759 // reported in the previous loop.
760 EXPECT_TRUE(enum_entry != histograms_xml_switches_ids.end() &&
asvitkinebff63b22014-11-25 01:03:52761 enum_entry->first == flag)
[email protected]4b66a7d2014-08-15 17:13:31762 << "histograms.xml enum LoginCustomFlags doesn't contain switch '"
asvitkinebff63b22014-11-25 01:03:52763 << flag << "' (value=" << uma_id
[email protected]4b66a7d2014-08-15 17:13:31764 << " expected). Consider adding entry:\n"
asvitkinebff63b22014-11-25 01:03:52765 << " " << GetHistogramEnumEntryText(flag, uma_id);
[email protected]4b66a7d2014-08-15 17:13:31766 }
767}
768
[email protected]e2ddbc92010-10-15 20:02:07769} // namespace about_flags