Re-enable child account detection, now behind a flag.
[chromium-blink-merge.git] / chrome / browser / about_flags_unittest.cc
blob1c1c6f16d25dc15bd09713ec0fe134ea02e826d4
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.
5 #include <stdint.h>
6 #include <utility>
8 #include "base/files/file_path.h"
9 #include "base/memory/scoped_vector.h"
10 #include "base/path_service.h"
11 #include "base/prefs/pref_registry_simple.h"
12 #include "base/prefs/testing_pref_service.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/about_flags.h"
18 #include "chrome/browser/pref_service_flags_storage.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/grit/chromium_strings.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "third_party/libxml/chromium/libxml_utils.h"
25 namespace {
27 const char kFlags1[] = "flag1";
28 const char kFlags2[] = "flag2";
29 const char kFlags3[] = "flag3";
30 const char kFlags4[] = "flag4";
31 const char kFlags5[] = "flag5";
33 const char kSwitch1[] = "switch";
34 const char kSwitch2[] = "switch2";
35 const char kSwitch3[] = "switch3";
36 const char kValueForSwitch2[] = "value_for_switch2";
38 const char kMultiSwitch1[] = "multi_switch1";
39 const char kMultiSwitch2[] = "multi_switch2";
40 const char kValueForMultiSwitch2[] = "value_for_multi_switch2";
42 const char kEnableDisableValue1[] = "value1";
43 const char kEnableDisableValue2[] = "value2";
45 typedef base::HistogramBase::Sample Sample;
46 typedef std::map<std::string, Sample> SwitchToIdMap;
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.
53 std::map<Sample, std::string> ParseEnumFromHistogramsXml(
54 const std::string& enum_name,
55 XmlReader* reader) {
56 int entries_index = -1;
58 std::map<Sample, std::string> result;
59 bool success = true;
61 while (true) {
62 const std::string node_name = reader->NodeName();
63 if (node_name == "enum" && reader->IsClosingElement())
64 break;
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;
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;
85 Sample value;
86 if (has_value && !base::StringToInt(value_str, &value)) {
87 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;
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;
102 if (success) {
103 result[value] = label;
106 // All enum entries are on the same level, so it is enough to iterate
107 // until possible.
108 reader->Next();
110 return (success ? result : std::map<Sample, std::string>());
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.
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.
122 std::map<Sample, std::string> ReadEnumFromHistogramsXml(
123 const std::string& enum_name,
124 XmlReader* histograms_xml) {
125 std::map<Sample, std::string> login_custom_flags;
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";
136 return std::map<Sample, std::string>();
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.";
151 if (login_custom_flags.empty())
152 return std::map<Sample, std::string>();
155 // Go deeper if possible (stops at the closing tag of the deepest node).
156 if (histograms_xml->Read())
157 continue;
159 // Try next node on the same level (skips closing tag).
160 if (histograms_xml->Next())
161 continue;
163 // Go up until next node on the same level exists.
164 while (histograms_xml->Depth() && !histograms_xml->SkipToElement()) {
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;
172 EXPECT_FALSE(login_custom_flags.empty())
173 << "Enum '" << enum_name << "' is not found in histograms.xml.";
174 return login_custom_flags;
177 std::string FilePathStringTypeToString(const base::FilePath::StringType& path) {
178 #if defined(OS_WIN)
179 return base::UTF16ToUTF8(path);
180 #else
181 return path;
182 #endif
185 std::set<std::string> GetAllSwitchesForTesting() {
186 std::set<std::string> result;
188 size_t num_experiments = 0;
189 const about_flags::Experiment* experiments =
190 about_flags::testing::GetExperiments(&num_experiments);
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);
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);
206 return result;
209 } // anonymous namespace
211 namespace about_flags {
213 const Experiment::Choice kMultiChoices[] = {
214 { IDS_PRODUCT_NAME, "", "" },
215 { IDS_PRODUCT_NAME, kMultiSwitch1, "" },
216 { IDS_PRODUCT_NAME, kMultiSwitch2, kValueForMultiSwitch2 },
219 // The experiments that are set for these tests. The 3rd experiment is not
220 // supported on the current platform, all others are.
221 static Experiment kExperiments[] = {
223 kFlags1,
224 IDS_PRODUCT_NAME,
225 IDS_PRODUCT_NAME,
226 0, // Ends up being mapped to the current platform.
227 Experiment::SINGLE_VALUE,
228 kSwitch1,
230 NULL,
231 NULL,
232 NULL,
236 kFlags2,
237 IDS_PRODUCT_NAME,
238 IDS_PRODUCT_NAME,
239 0, // Ends up being mapped to the current platform.
240 Experiment::SINGLE_VALUE,
241 kSwitch2,
242 kValueForSwitch2,
243 NULL,
244 NULL,
245 NULL,
249 kFlags3,
250 IDS_PRODUCT_NAME,
251 IDS_PRODUCT_NAME,
252 0, // This ends up enabling for an OS other than the current.
253 Experiment::SINGLE_VALUE,
254 kSwitch3,
256 NULL,
257 NULL,
258 NULL,
262 kFlags4,
263 IDS_PRODUCT_NAME,
264 IDS_PRODUCT_NAME,
265 0, // Ends up being mapped to the current platform.
266 Experiment::MULTI_VALUE,
271 kMultiChoices,
272 arraysize(kMultiChoices)
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,
289 class AboutFlagsTest : public ::testing::Test {
290 protected:
291 AboutFlagsTest() : flags_storage_(&prefs_) {
292 prefs_.registry()->RegisterListPref(prefs::kEnabledLabsExperiments);
293 testing::ClearState();
296 void SetUp() override {
297 for (size_t i = 0; i < arraysize(kExperiments); ++i)
298 kExperiments[i].supported_platforms = GetCurrentPlatform();
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;
305 testing::SetExperiments(kExperiments, arraysize(kExperiments));
308 void TearDown() override { testing::SetExperiments(NULL, 0); }
310 TestingPrefServiceSimple prefs_;
311 PrefServiceFlagsStorage flags_storage_;
315 TEST_F(AboutFlagsTest, NoChangeNoRestart) {
316 EXPECT_FALSE(IsRestartNeededToCommitChanges());
317 SetExperimentEnabled(&flags_storage_, kFlags1, false);
318 EXPECT_FALSE(IsRestartNeededToCommitChanges());
321 TEST_F(AboutFlagsTest, ChangeNeedsRestart) {
322 EXPECT_FALSE(IsRestartNeededToCommitChanges());
323 SetExperimentEnabled(&flags_storage_, kFlags1, true);
324 EXPECT_TRUE(IsRestartNeededToCommitChanges());
327 TEST_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.
332 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
333 EXPECT_TRUE(IsRestartNeededToCommitChanges());
334 testing::ClearState();
335 EXPECT_FALSE(IsRestartNeededToCommitChanges());
336 // Enable the default choice now.
337 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
338 EXPECT_TRUE(IsRestartNeededToCommitChanges());
341 TEST_F(AboutFlagsTest, AddTwoFlagsRemoveOne) {
342 // Add two experiments, check they're there.
343 SetExperimentEnabled(&flags_storage_, kFlags1, true);
344 SetExperimentEnabled(&flags_storage_, kFlags2, true);
346 const base::ListValue* experiments_list = prefs_.GetList(
347 prefs::kEnabledLabsExperiments);
348 ASSERT_TRUE(experiments_list != NULL);
350 ASSERT_EQ(2u, experiments_list->GetSize());
352 std::string s0;
353 ASSERT_TRUE(experiments_list->GetString(0, &s0));
354 std::string s1;
355 ASSERT_TRUE(experiments_list->GetString(1, &s1));
357 EXPECT_TRUE(s0 == kFlags1 || s1 == kFlags1);
358 EXPECT_TRUE(s0 == kFlags2 || s1 == kFlags2);
360 // Remove one experiment, check the other's still around.
361 SetExperimentEnabled(&flags_storage_, kFlags2, false);
363 experiments_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
364 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);
370 TEST_F(AboutFlagsTest, AddTwoFlagsRemoveBoth) {
371 // Add two experiments, check the pref exists.
372 SetExperimentEnabled(&flags_storage_, kFlags1, true);
373 SetExperimentEnabled(&flags_storage_, kFlags2, true);
374 const base::ListValue* experiments_list = prefs_.GetList(
375 prefs::kEnabledLabsExperiments);
376 ASSERT_TRUE(experiments_list != NULL);
378 // Remove both, the pref should have been removed completely.
379 SetExperimentEnabled(&flags_storage_, kFlags1, false);
380 SetExperimentEnabled(&flags_storage_, kFlags2, false);
381 experiments_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
382 EXPECT_TRUE(experiments_list == NULL || experiments_list->GetSize() == 0);
385 TEST_F(AboutFlagsTest, ConvertFlagsToSwitches) {
386 SetExperimentEnabled(&flags_storage_, kFlags1, true);
388 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
389 command_line.AppendSwitch("foo");
391 EXPECT_TRUE(command_line.HasSwitch("foo"));
392 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
394 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
396 EXPECT_TRUE(command_line.HasSwitch("foo"));
397 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
398 EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesBegin));
399 EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesEnd));
401 base::CommandLine command_line2(base::CommandLine::NO_PROGRAM);
403 ConvertFlagsToSwitches(&flags_storage_, &command_line2, kNoSentinels);
405 EXPECT_TRUE(command_line2.HasSwitch(kSwitch1));
406 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesBegin));
407 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesEnd));
410 base::CommandLine::StringType CreateSwitch(const std::string& value) {
411 #if defined(OS_WIN)
412 return base::ASCIIToUTF16(value);
413 #else
414 return value;
415 #endif
418 TEST_F(AboutFlagsTest, CompareSwitchesToCurrentCommandLine) {
419 SetExperimentEnabled(&flags_storage_, kFlags1, true);
421 const std::string kDoubleDash("--");
423 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
424 command_line.AppendSwitch("foo");
426 base::CommandLine new_command_line(base::CommandLine::NO_PROGRAM);
427 ConvertFlagsToSwitches(&flags_storage_, &new_command_line, kAddSentinels);
429 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
430 new_command_line, command_line, NULL));
432 std::set<base::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)));
439 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
441 EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(
442 new_command_line, command_line, NULL));
444 std::set<base::CommandLine::StringType> difference;
445 EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(
446 new_command_line, command_line, &difference));
447 EXPECT_TRUE(difference.empty());
450 // Now both have flags but different.
451 SetExperimentEnabled(&flags_storage_, kFlags1, false);
452 SetExperimentEnabled(&flags_storage_, kFlags2, true);
454 base::CommandLine another_command_line(base::CommandLine::NO_PROGRAM);
455 ConvertFlagsToSwitches(&flags_storage_, &another_command_line, kAddSentinels);
457 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
458 new_command_line, another_command_line, NULL));
460 std::set<base::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)));
471 TEST_F(AboutFlagsTest, RemoveFlagSwitches) {
472 std::map<std::string, base::CommandLine::StringType> switch_list;
473 switch_list[kSwitch1] = base::CommandLine::StringType();
474 switch_list[switches::kFlagSwitchesBegin] = base::CommandLine::StringType();
475 switch_list[switches::kFlagSwitchesEnd] = base::CommandLine::StringType();
476 switch_list["foo"] = base::CommandLine::StringType();
478 SetExperimentEnabled(&flags_storage_, kFlags1, true);
480 // This shouldn't do anything before ConvertFlagsToSwitches() wasn't called.
481 RemoveFlagsSwitches(&switch_list);
482 ASSERT_EQ(4u, switch_list.size());
483 EXPECT_TRUE(switch_list.find(kSwitch1) != switch_list.end());
484 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());
490 // Call ConvertFlagsToSwitches(), then RemoveFlagsSwitches() again.
491 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
492 command_line.AppendSwitch("foo");
493 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
494 RemoveFlagsSwitches(&switch_list);
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());
501 // Tests enabling experiments that aren't supported on the current platform.
502 TEST_F(AboutFlagsTest, PersistAndPrune) {
503 // Enable experiments 1 and 3.
504 SetExperimentEnabled(&flags_storage_, kFlags1, true);
505 SetExperimentEnabled(&flags_storage_, kFlags3, true);
506 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
507 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
508 EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
510 // Convert the flags to switches. Experiment 3 shouldn't be among the switches
511 // as it is not applicable to the current platform.
512 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
513 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
514 EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
516 // Experiment 3 should show still be persisted in preferences though.
517 const base::ListValue* experiments_list =
518 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);
529 // Tests that switches which should have values get them in the command
530 // line.
531 TEST_F(AboutFlagsTest, CheckValues) {
532 // Enable experiments 1 and 2.
533 SetExperimentEnabled(&flags_storage_, kFlags1, true);
534 SetExperimentEnabled(&flags_storage_, kFlags2, true);
535 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
536 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
537 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
539 // Convert the flags to switches.
540 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
541 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
542 EXPECT_EQ(std::string(), command_line.GetSwitchValueASCII(kSwitch1));
543 EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
544 EXPECT_EQ(std::string(kValueForSwitch2),
545 command_line.GetSwitchValueASCII(kSwitch2));
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)
552 EXPECT_EQ(base::string16::npos,
553 command_line.GetCommandLineString().find(
554 base::ASCIIToUTF16(switch1_with_equals)));
555 #else
556 EXPECT_EQ(std::string::npos,
557 command_line.GetCommandLineString().find(switch1_with_equals));
558 #endif
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("=");
564 #if defined(OS_WIN)
565 EXPECT_NE(base::string16::npos,
566 command_line.GetCommandLineString().find(
567 base::ASCIIToUTF16(switch2_with_equals)));
568 #else
569 EXPECT_NE(std::string::npos,
570 command_line.GetCommandLineString().find(switch2_with_equals));
571 #endif
573 // And it should persist.
574 const base::ListValue* experiments_list =
575 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);
586 // Tests multi-value type experiments.
587 TEST_F(AboutFlagsTest, MultiValues) {
588 const Experiment& experiment = kExperiments[3];
589 ASSERT_EQ(kFlags4, experiment.internal_name);
591 // Initially, the first "deactivated" option of the multi experiment should
592 // be set.
594 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
595 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
596 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
597 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
600 // Enable the 2nd choice of the multi-value.
601 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
603 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
604 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
605 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
606 EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch2));
607 EXPECT_EQ(std::string(kValueForMultiSwitch2),
608 command_line.GetSwitchValueASCII(kMultiSwitch2));
611 // Disable the multi-value experiment.
612 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
614 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
615 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
616 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
617 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
621 TEST_F(AboutFlagsTest, EnableDisableValues) {
622 const Experiment& experiment = kExperiments[4];
623 ASSERT_EQ(kFlags5, experiment.internal_name);
625 // Nothing selected.
627 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
628 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
629 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
630 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
633 // "Enable" option selected.
634 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(1), true);
636 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
637 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
638 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
639 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
640 EXPECT_EQ(kEnableDisableValue1, command_line.GetSwitchValueASCII(kSwitch1));
643 // "Disable" option selected.
644 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
646 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
647 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
648 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
649 EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
650 EXPECT_EQ(kEnableDisableValue2, command_line.GetSwitchValueASCII(kSwitch2));
653 // "Default" option selected, same as nothing selected.
654 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
656 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
657 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
658 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
659 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
663 // Makes sure there are no separators in any of the experiment names.
664 TEST_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;
674 class 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,
679 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 =
682 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.";
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,
693 Sample value) {
694 return base::StringPrintf(
695 "<int value=\"%d\" label=\"%s\"/>", value, switch_name.c_str());
699 TEST_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");
708 XmlReader histograms_xml;
709 ASSERT_TRUE(histograms_xml.LoadFile(
710 FilePathStringTypeToString(histograms_xml_file_path.value())));
711 std::map<Sample, std::string> login_custom_flags =
712 ReadEnumFromHistogramsXml("LoginCustomFlags", &histograms_xml);
713 ASSERT_TRUE(login_custom_flags.size())
714 << "Error reading enum 'LoginCustomFlags' from histograms.xml.";
716 // Build reverse map {switch_name => id} from login_custom_flags.
717 SwitchToIdMap histograms_xml_switches_ids;
719 EXPECT_TRUE(login_custom_flags.count(testing::kBadSwitchFormatHistogramId))
720 << "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.
725 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);
730 continue;
732 const Sample uma_id = GetSwitchUMAId(entry.second);
733 EXPECT_EQ(uma_id, entry.first)
734 << "histograms.xml enum LoginCustomFlags "
735 "entry '" << entry.second << "' has incorrect value=" << entry.first
736 << ", but " << uma_id << " is expected. Consider changing entry to:\n"
737 << " " << GetHistogramEnumEntryText(entry.second, uma_id);
738 SetSwitchToHistogramIdMapping(entry.second, entry.first,
739 &histograms_xml_switches_ids);
742 // Check that all flags in about_flags.cc have entries in login_custom_flags.
743 std::set<std::string> all_switches = GetAllSwitchesForTesting();
744 for (const std::string& flag : all_switches) {
745 // Skip empty placeholders.
746 if (flag.empty())
747 continue;
748 const Sample uma_id = GetSwitchUMAId(flag);
749 EXPECT_NE(testing::kBadSwitchFormatHistogramId, uma_id)
750 << "Command-line switch '" << flag
751 << "' from about_flags.cc has UMA ID equal to reserved value "
752 "kBadSwitchFormatHistogramId="
753 << testing::kBadSwitchFormatHistogramId
754 << ". Please modify switch name.";
755 SwitchToIdMap::iterator enum_entry =
756 histograms_xml_switches_ids.lower_bound(flag);
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() &&
761 enum_entry->first == flag)
762 << "histograms.xml enum LoginCustomFlags doesn't contain switch '"
763 << flag << "' (value=" << uma_id
764 << " expected). Consider adding entry:\n"
765 << " " << GetHistogramEnumEntryText(flag, uma_id);
769 } // namespace about_flags