Allow the minimum time between banner trigger visits to be varied via field trials.
[chromium-blink-merge.git] / chrome / browser / about_flags_unittest.cc
blob07b7d40a4a17b81b9c7c2aba339ee715906c4874
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 "chrome/browser/about_flags.h"
7 #include <stdint.h>
8 #include <utility>
10 #include "base/files/file_path.h"
11 #include "base/path_service.h"
12 #include "base/prefs/pref_registry_simple.h"
13 #include "base/prefs/testing_pref_service.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.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 about_flags {
27 namespace {
29 const char kFlags1[] = "flag1";
30 const char kFlags2[] = "flag2";
31 const char kFlags3[] = "flag3";
32 const char kFlags4[] = "flag4";
33 const char kFlags5[] = "flag5";
34 const char kFlags6[] = "flag6";
36 const char kSwitch1[] = "switch";
37 const char kSwitch2[] = "switch2";
38 const char kSwitch3[] = "switch3";
39 const char kSwitch6[] = "switch6";
40 const char kValueForSwitch2[] = "value_for_switch2";
42 const char kMultiSwitch1[] = "multi_switch1";
43 const char kMultiSwitch2[] = "multi_switch2";
44 const char kValueForMultiSwitch2[] = "value_for_multi_switch2";
46 const char kEnableDisableValue1[] = "value1";
47 const char kEnableDisableValue2[] = "value2";
49 typedef base::HistogramBase::Sample Sample;
50 typedef std::map<std::string, Sample> SwitchToIdMap;
52 // This is a helper function to the ReadEnumFromHistogramsXml().
53 // Extracts single enum (with integer values) from histograms.xml.
54 // Expects |reader| to point at given enum.
55 // Returns map { value => label }.
56 // Returns empty map on error.
57 std::map<Sample, std::string> ParseEnumFromHistogramsXml(
58 const std::string& enum_name,
59 XmlReader* reader) {
60 int entries_index = -1;
62 std::map<Sample, std::string> result;
63 bool success = true;
65 while (true) {
66 const std::string node_name = reader->NodeName();
67 if (node_name == "enum" && reader->IsClosingElement())
68 break;
70 if (node_name == "int") {
71 ++entries_index;
72 std::string value_str;
73 std::string label;
74 const bool has_value = reader->NodeAttribute("value", &value_str);
75 const bool has_label = reader->NodeAttribute("label", &label);
76 if (!has_value) {
77 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
78 << entries_index << ", label='" << label
79 << "'): No 'value' attribute.";
80 success = false;
82 if (!has_label) {
83 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
84 << entries_index << ", value_str='" << value_str
85 << "'): No 'label' attribute.";
86 success = false;
89 Sample value;
90 if (has_value && !base::StringToInt(value_str, &value)) {
91 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
92 << entries_index << ", label='" << label
93 << "', value_str='" << value_str
94 << "'): 'value' attribute is not integer.";
95 success = false;
97 if (result.count(value)) {
98 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
99 << entries_index << ", label='" << label
100 << "', value_str='" << value_str
101 << "'): duplicate value '" << value_str
102 << "' found in enum. The previous one has label='"
103 << result[value] << "'.";
104 success = false;
106 if (success) {
107 result[value] = label;
110 // All enum entries are on the same level, so it is enough to iterate
111 // until possible.
112 reader->Next();
114 return (success ? result : std::map<Sample, std::string>());
117 // Find and read given enum (with integer values) from histograms.xml.
118 // |enum_name| - enum name.
119 // |histograms_xml| - must be loaded histograms.xml file.
121 // Returns map { value => label } so that:
122 // <int value="9" label="enable-pinch-virtual-viewport"/>
123 // becomes:
124 // { 9 => "enable-pinch-virtual-viewport" }
125 // Returns empty map on error.
126 std::map<Sample, std::string> ReadEnumFromHistogramsXml(
127 const std::string& enum_name,
128 XmlReader* histograms_xml) {
129 std::map<Sample, std::string> login_custom_flags;
131 // Implement simple depth first search.
132 while (true) {
133 const std::string node_name = histograms_xml->NodeName();
134 if (node_name == "enum") {
135 std::string name;
136 if (histograms_xml->NodeAttribute("name", &name) && name == enum_name) {
137 if (!login_custom_flags.empty()) {
138 EXPECT_TRUE(login_custom_flags.empty())
139 << "Duplicate enum '" << enum_name << "' found in histograms.xml";
140 return std::map<Sample, std::string>();
143 const bool got_into_enum = histograms_xml->Read();
144 if (got_into_enum) {
145 login_custom_flags =
146 ParseEnumFromHistogramsXml(enum_name, histograms_xml);
147 EXPECT_FALSE(login_custom_flags.empty())
148 << "Bad enum '" << enum_name
149 << "' found in histograms.xml (format error).";
150 } else {
151 EXPECT_TRUE(got_into_enum)
152 << "Bad enum '" << enum_name
153 << "' (looks empty) found in histograms.xml.";
155 if (login_custom_flags.empty())
156 return std::map<Sample, std::string>();
159 // Go deeper if possible (stops at the closing tag of the deepest node).
160 if (histograms_xml->Read())
161 continue;
163 // Try next node on the same level (skips closing tag).
164 if (histograms_xml->Next())
165 continue;
167 // Go up until next node on the same level exists.
168 while (histograms_xml->Depth() && !histograms_xml->SkipToElement()) {
171 // Reached top. histograms.xml consists of the single top level node
172 // 'histogram-configuration', so this is the end.
173 if (!histograms_xml->Depth())
174 break;
176 EXPECT_FALSE(login_custom_flags.empty())
177 << "Enum '" << enum_name << "' is not found in histograms.xml.";
178 return login_custom_flags;
181 std::string FilePathStringTypeToString(const base::FilePath::StringType& path) {
182 #if defined(OS_WIN)
183 return base::UTF16ToUTF8(path);
184 #else
185 return path;
186 #endif
189 std::set<std::string> GetAllSwitchesForTesting() {
190 std::set<std::string> result;
192 size_t num_experiments = 0;
193 const Experiment* experiments =
194 testing::GetExperiments(&num_experiments);
196 for (size_t i = 0; i < num_experiments; ++i) {
197 const Experiment& experiment = experiments[i];
198 if (experiment.type == Experiment::SINGLE_VALUE ||
199 experiment.type == Experiment::SINGLE_DISABLE_VALUE) {
200 result.insert(experiment.command_line_switch);
201 } else if (experiment.type == Experiment::MULTI_VALUE) {
202 for (int j = 0; j < experiment.num_choices; ++j) {
203 result.insert(experiment.choices[j].command_line_switch);
205 } else {
206 DCHECK_EQ(experiment.type, Experiment::ENABLE_DISABLE_VALUE);
207 result.insert(experiment.command_line_switch);
208 result.insert(experiment.disable_command_line_switch);
211 return result;
214 } // anonymous namespace
216 const Experiment::Choice kMultiChoices[] = {
217 { IDS_PRODUCT_NAME, "", "" },
218 { IDS_PRODUCT_NAME, kMultiSwitch1, "" },
219 { IDS_PRODUCT_NAME, kMultiSwitch2, kValueForMultiSwitch2 },
222 // The experiments that are set for these tests. The 3rd experiment is not
223 // supported on the current platform, all others are.
224 static Experiment kExperiments[] = {
226 kFlags1,
227 IDS_PRODUCT_NAME,
228 IDS_PRODUCT_NAME,
229 0, // Ends up being mapped to the current platform.
230 Experiment::SINGLE_VALUE,
231 kSwitch1,
233 NULL,
234 NULL,
235 NULL,
239 kFlags2,
240 IDS_PRODUCT_NAME,
241 IDS_PRODUCT_NAME,
242 0, // Ends up being mapped to the current platform.
243 Experiment::SINGLE_VALUE,
244 kSwitch2,
245 kValueForSwitch2,
246 NULL,
247 NULL,
248 NULL,
252 kFlags3,
253 IDS_PRODUCT_NAME,
254 IDS_PRODUCT_NAME,
255 0, // This ends up enabling for an OS other than the current.
256 Experiment::SINGLE_VALUE,
257 kSwitch3,
259 NULL,
260 NULL,
261 NULL,
265 kFlags4,
266 IDS_PRODUCT_NAME,
267 IDS_PRODUCT_NAME,
268 0, // Ends up being mapped to the current platform.
269 Experiment::MULTI_VALUE,
274 kMultiChoices,
275 arraysize(kMultiChoices)
278 kFlags5,
279 IDS_PRODUCT_NAME,
280 IDS_PRODUCT_NAME,
281 0, // Ends up being mapped to the current platform.
282 Experiment::ENABLE_DISABLE_VALUE,
283 kSwitch1,
284 kEnableDisableValue1,
285 kSwitch2,
286 kEnableDisableValue2,
287 NULL,
291 kFlags6,
292 IDS_PRODUCT_NAME,
293 IDS_PRODUCT_NAME,
295 Experiment::SINGLE_DISABLE_VALUE,
296 kSwitch6,
298 NULL,
299 NULL,
300 NULL,
305 class AboutFlagsTest : public ::testing::Test {
306 protected:
307 AboutFlagsTest() : flags_storage_(&prefs_) {
308 prefs_.registry()->RegisterListPref(prefs::kEnabledLabsExperiments);
309 testing::ClearState();
312 void SetUp() override {
313 for (size_t i = 0; i < arraysize(kExperiments); ++i)
314 kExperiments[i].supported_platforms = GetCurrentPlatform();
316 int os_other_than_current = 1;
317 while (os_other_than_current == GetCurrentPlatform())
318 os_other_than_current <<= 1;
319 kExperiments[2].supported_platforms = os_other_than_current;
321 testing::SetExperiments(kExperiments, arraysize(kExperiments));
324 void TearDown() override { testing::SetExperiments(NULL, 0); }
326 TestingPrefServiceSimple prefs_;
327 PrefServiceFlagsStorage flags_storage_;
331 TEST_F(AboutFlagsTest, NoChangeNoRestart) {
332 EXPECT_FALSE(IsRestartNeededToCommitChanges());
333 SetExperimentEnabled(&flags_storage_, kFlags1, false);
334 EXPECT_FALSE(IsRestartNeededToCommitChanges());
336 // kFlags6 is enabled by default, so enabling should not require a restart.
337 SetExperimentEnabled(&flags_storage_, kFlags6, true);
338 EXPECT_FALSE(IsRestartNeededToCommitChanges());
341 TEST_F(AboutFlagsTest, ChangeNeedsRestart) {
342 EXPECT_FALSE(IsRestartNeededToCommitChanges());
343 SetExperimentEnabled(&flags_storage_, kFlags1, true);
344 EXPECT_TRUE(IsRestartNeededToCommitChanges());
347 // Tests that disabling a default enabled experiment requires a restart.
348 TEST_F(AboutFlagsTest, DisableChangeNeedsRestart) {
349 EXPECT_FALSE(IsRestartNeededToCommitChanges());
350 SetExperimentEnabled(&flags_storage_, kFlags6, false);
351 EXPECT_TRUE(IsRestartNeededToCommitChanges());
354 TEST_F(AboutFlagsTest, MultiFlagChangeNeedsRestart) {
355 const Experiment& experiment = kExperiments[3];
356 ASSERT_EQ(kFlags4, experiment.internal_name);
357 EXPECT_FALSE(IsRestartNeededToCommitChanges());
358 // Enable the 2nd choice of the multi-value.
359 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
360 EXPECT_TRUE(IsRestartNeededToCommitChanges());
361 testing::ClearState();
362 EXPECT_FALSE(IsRestartNeededToCommitChanges());
363 // Enable the default choice now.
364 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
365 EXPECT_TRUE(IsRestartNeededToCommitChanges());
368 TEST_F(AboutFlagsTest, AddTwoFlagsRemoveOne) {
369 // Add two experiments, check they're there.
370 SetExperimentEnabled(&flags_storage_, kFlags1, true);
371 SetExperimentEnabled(&flags_storage_, kFlags2, true);
373 const base::ListValue* experiments_list = prefs_.GetList(
374 prefs::kEnabledLabsExperiments);
375 ASSERT_TRUE(experiments_list != NULL);
377 ASSERT_EQ(2u, experiments_list->GetSize());
379 std::string s0;
380 ASSERT_TRUE(experiments_list->GetString(0, &s0));
381 std::string s1;
382 ASSERT_TRUE(experiments_list->GetString(1, &s1));
384 EXPECT_TRUE(s0 == kFlags1 || s1 == kFlags1);
385 EXPECT_TRUE(s0 == kFlags2 || s1 == kFlags2);
387 // Remove one experiment, check the other's still around.
388 SetExperimentEnabled(&flags_storage_, kFlags2, false);
390 experiments_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
391 ASSERT_TRUE(experiments_list != NULL);
392 ASSERT_EQ(1u, experiments_list->GetSize());
393 ASSERT_TRUE(experiments_list->GetString(0, &s0));
394 EXPECT_TRUE(s0 == kFlags1);
397 TEST_F(AboutFlagsTest, AddTwoFlagsRemoveBoth) {
398 // Add two experiments, check the pref exists.
399 SetExperimentEnabled(&flags_storage_, kFlags1, true);
400 SetExperimentEnabled(&flags_storage_, kFlags2, true);
401 const base::ListValue* experiments_list = prefs_.GetList(
402 prefs::kEnabledLabsExperiments);
403 ASSERT_TRUE(experiments_list != NULL);
405 // Remove both, the pref should have been removed completely.
406 SetExperimentEnabled(&flags_storage_, kFlags1, false);
407 SetExperimentEnabled(&flags_storage_, kFlags2, false);
408 experiments_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
409 EXPECT_TRUE(experiments_list == NULL || experiments_list->GetSize() == 0);
412 TEST_F(AboutFlagsTest, ConvertFlagsToSwitches) {
413 SetExperimentEnabled(&flags_storage_, kFlags1, true);
415 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
416 command_line.AppendSwitch("foo");
418 EXPECT_TRUE(command_line.HasSwitch("foo"));
419 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
421 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
423 EXPECT_TRUE(command_line.HasSwitch("foo"));
424 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
425 EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesBegin));
426 EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesEnd));
428 base::CommandLine command_line2(base::CommandLine::NO_PROGRAM);
430 ConvertFlagsToSwitches(&flags_storage_, &command_line2, kNoSentinels);
432 EXPECT_TRUE(command_line2.HasSwitch(kSwitch1));
433 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesBegin));
434 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesEnd));
437 base::CommandLine::StringType CreateSwitch(const std::string& value) {
438 #if defined(OS_WIN)
439 return base::ASCIIToUTF16(value);
440 #else
441 return value;
442 #endif
445 TEST_F(AboutFlagsTest, CompareSwitchesToCurrentCommandLine) {
446 SetExperimentEnabled(&flags_storage_, kFlags1, true);
448 const std::string kDoubleDash("--");
450 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
451 command_line.AppendSwitch("foo");
453 base::CommandLine new_command_line(base::CommandLine::NO_PROGRAM);
454 ConvertFlagsToSwitches(&flags_storage_, &new_command_line, kAddSentinels);
456 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
457 new_command_line, command_line, NULL));
459 std::set<base::CommandLine::StringType> difference;
460 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
461 new_command_line, command_line, &difference));
462 EXPECT_EQ(1U, difference.size());
463 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch1)));
466 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
468 EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(
469 new_command_line, command_line, NULL));
471 std::set<base::CommandLine::StringType> difference;
472 EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(
473 new_command_line, command_line, &difference));
474 EXPECT_TRUE(difference.empty());
477 // Now both have flags but different.
478 SetExperimentEnabled(&flags_storage_, kFlags1, false);
479 SetExperimentEnabled(&flags_storage_, kFlags2, true);
481 base::CommandLine another_command_line(base::CommandLine::NO_PROGRAM);
482 ConvertFlagsToSwitches(&flags_storage_, &another_command_line, kAddSentinels);
484 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
485 new_command_line, another_command_line, NULL));
487 std::set<base::CommandLine::StringType> difference;
488 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
489 new_command_line, another_command_line, &difference));
490 EXPECT_EQ(2U, difference.size());
491 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch1)));
492 EXPECT_EQ(1U,
493 difference.count(CreateSwitch(kDoubleDash + kSwitch2 + "=" +
494 kValueForSwitch2)));
498 TEST_F(AboutFlagsTest, RemoveFlagSwitches) {
499 std::map<std::string, base::CommandLine::StringType> switch_list;
500 switch_list[kSwitch1] = base::CommandLine::StringType();
501 switch_list[switches::kFlagSwitchesBegin] = base::CommandLine::StringType();
502 switch_list[switches::kFlagSwitchesEnd] = base::CommandLine::StringType();
503 switch_list["foo"] = base::CommandLine::StringType();
505 SetExperimentEnabled(&flags_storage_, kFlags1, true);
507 // This shouldn't do anything before ConvertFlagsToSwitches() wasn't called.
508 RemoveFlagsSwitches(&switch_list);
509 ASSERT_EQ(4u, switch_list.size());
510 EXPECT_TRUE(switch_list.find(kSwitch1) != switch_list.end());
511 EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesBegin) !=
512 switch_list.end());
513 EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesEnd) !=
514 switch_list.end());
515 EXPECT_TRUE(switch_list.find("foo") != switch_list.end());
517 // Call ConvertFlagsToSwitches(), then RemoveFlagsSwitches() again.
518 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
519 command_line.AppendSwitch("foo");
520 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
521 RemoveFlagsSwitches(&switch_list);
523 // Now the about:flags-related switch should have been removed.
524 ASSERT_EQ(1u, switch_list.size());
525 EXPECT_TRUE(switch_list.find("foo") != switch_list.end());
528 // Tests enabling experiments that aren't supported on the current platform.
529 TEST_F(AboutFlagsTest, PersistAndPrune) {
530 // Enable experiments 1 and 3.
531 SetExperimentEnabled(&flags_storage_, kFlags1, true);
532 SetExperimentEnabled(&flags_storage_, kFlags3, true);
533 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
534 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
535 EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
537 // Convert the flags to switches. Experiment 3 shouldn't be among the switches
538 // as it is not applicable to the current platform.
539 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
540 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
541 EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
543 // Experiment 3 should show still be persisted in preferences though.
544 const base::ListValue* experiments_list =
545 prefs_.GetList(prefs::kEnabledLabsExperiments);
546 ASSERT_TRUE(experiments_list);
547 EXPECT_EQ(2U, experiments_list->GetSize());
548 std::string s0;
549 ASSERT_TRUE(experiments_list->GetString(0, &s0));
550 EXPECT_EQ(kFlags1, s0);
551 std::string s1;
552 ASSERT_TRUE(experiments_list->GetString(1, &s1));
553 EXPECT_EQ(kFlags3, s1);
556 // Tests that switches which should have values get them in the command
557 // line.
558 TEST_F(AboutFlagsTest, CheckValues) {
559 // Enable experiments 1 and 2.
560 SetExperimentEnabled(&flags_storage_, kFlags1, true);
561 SetExperimentEnabled(&flags_storage_, kFlags2, true);
562 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
563 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
564 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
566 // Convert the flags to switches.
567 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
568 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
569 EXPECT_EQ(std::string(), command_line.GetSwitchValueASCII(kSwitch1));
570 EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
571 EXPECT_EQ(std::string(kValueForSwitch2),
572 command_line.GetSwitchValueASCII(kSwitch2));
574 // Confirm that there is no '=' in the command line for simple switches.
575 std::string switch1_with_equals = std::string("--") +
576 std::string(kSwitch1) +
577 std::string("=");
578 #if defined(OS_WIN)
579 EXPECT_EQ(base::string16::npos,
580 command_line.GetCommandLineString().find(
581 base::ASCIIToUTF16(switch1_with_equals)));
582 #else
583 EXPECT_EQ(std::string::npos,
584 command_line.GetCommandLineString().find(switch1_with_equals));
585 #endif
587 // And confirm there is a '=' for switches with values.
588 std::string switch2_with_equals = std::string("--") +
589 std::string(kSwitch2) +
590 std::string("=");
591 #if defined(OS_WIN)
592 EXPECT_NE(base::string16::npos,
593 command_line.GetCommandLineString().find(
594 base::ASCIIToUTF16(switch2_with_equals)));
595 #else
596 EXPECT_NE(std::string::npos,
597 command_line.GetCommandLineString().find(switch2_with_equals));
598 #endif
600 // And it should persist.
601 const base::ListValue* experiments_list =
602 prefs_.GetList(prefs::kEnabledLabsExperiments);
603 ASSERT_TRUE(experiments_list);
604 EXPECT_EQ(2U, experiments_list->GetSize());
605 std::string s0;
606 ASSERT_TRUE(experiments_list->GetString(0, &s0));
607 EXPECT_EQ(kFlags1, s0);
608 std::string s1;
609 ASSERT_TRUE(experiments_list->GetString(1, &s1));
610 EXPECT_EQ(kFlags2, s1);
613 // Tests multi-value type experiments.
614 TEST_F(AboutFlagsTest, MultiValues) {
615 const Experiment& experiment = kExperiments[3];
616 ASSERT_EQ(kFlags4, experiment.internal_name);
618 // Initially, the first "deactivated" option of the multi experiment should
619 // be set.
621 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
622 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
623 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
624 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
627 // Enable the 2nd choice of the multi-value.
628 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
630 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
631 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
632 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
633 EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch2));
634 EXPECT_EQ(std::string(kValueForMultiSwitch2),
635 command_line.GetSwitchValueASCII(kMultiSwitch2));
638 // Disable the multi-value experiment.
639 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
641 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
642 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
643 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
644 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
648 // Tests that disable flags are added when an experiment is disabled.
649 TEST_F(AboutFlagsTest, DisableFlagCommandLine) {
650 // Nothing selected.
652 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
653 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
654 EXPECT_FALSE(command_line.HasSwitch(kSwitch6));
657 // Disable the experiment 6.
658 SetExperimentEnabled(&flags_storage_, kFlags6, false);
660 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
661 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
662 EXPECT_TRUE(command_line.HasSwitch(kSwitch6));
665 // Enable experiment 6.
666 SetExperimentEnabled(&flags_storage_, kFlags6, true);
668 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
669 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
670 EXPECT_FALSE(command_line.HasSwitch(kSwitch6));
674 TEST_F(AboutFlagsTest, EnableDisableValues) {
675 const Experiment& experiment = kExperiments[4];
676 ASSERT_EQ(kFlags5, experiment.internal_name);
678 // Nothing selected.
680 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
681 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
682 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
683 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
686 // "Enable" option selected.
687 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(1), true);
689 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
690 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
691 EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
692 EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
693 EXPECT_EQ(kEnableDisableValue1, command_line.GetSwitchValueASCII(kSwitch1));
696 // "Disable" option selected.
697 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
699 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
700 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
701 EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
702 EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
703 EXPECT_EQ(kEnableDisableValue2, command_line.GetSwitchValueASCII(kSwitch2));
706 // "Default" option selected, same as nothing selected.
707 SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
709 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
710 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
711 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
712 EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
716 // Makes sure there are no separators in any of the experiment names.
717 TEST_F(AboutFlagsTest, NoSeparators) {
718 testing::SetExperiments(NULL, 0);
719 size_t count;
720 const Experiment* experiments = testing::GetExperiments(&count);
721 for (size_t i = 0; i < count; ++i) {
722 std::string name = experiments->internal_name;
723 EXPECT_EQ(std::string::npos, name.find(testing::kMultiSeparator)) << i;
727 class AboutFlagsHistogramTest : public ::testing::Test {
728 protected:
729 // This is a helper function to check that all IDs in enum LoginCustomFlags in
730 // histograms.xml are unique.
731 void SetSwitchToHistogramIdMapping(const std::string& switch_name,
732 const Sample switch_histogram_id,
733 std::map<std::string, Sample>* out_map) {
734 const std::pair<std::map<std::string, Sample>::iterator, bool> status =
735 out_map->insert(std::make_pair(switch_name, switch_histogram_id));
736 if (!status.second) {
737 EXPECT_TRUE(status.first->second == switch_histogram_id)
738 << "Duplicate switch '" << switch_name
739 << "' found in enum 'LoginCustomFlags' in histograms.xml.";
743 // This method generates a hint for the user for what string should be added
744 // to the enum LoginCustomFlags to make in consistent.
745 std::string GetHistogramEnumEntryText(const std::string& switch_name,
746 Sample value) {
747 return base::StringPrintf(
748 "<int value=\"%d\" label=\"%s\"/>", value, switch_name.c_str());
752 TEST_F(AboutFlagsHistogramTest, CheckHistograms) {
753 base::FilePath histograms_xml_file_path;
754 ASSERT_TRUE(
755 PathService::Get(base::DIR_SOURCE_ROOT, &histograms_xml_file_path));
756 histograms_xml_file_path = histograms_xml_file_path.AppendASCII("tools")
757 .AppendASCII("metrics")
758 .AppendASCII("histograms")
759 .AppendASCII("histograms.xml");
761 XmlReader histograms_xml;
762 ASSERT_TRUE(histograms_xml.LoadFile(
763 FilePathStringTypeToString(histograms_xml_file_path.value())));
764 std::map<Sample, std::string> login_custom_flags =
765 ReadEnumFromHistogramsXml("LoginCustomFlags", &histograms_xml);
766 ASSERT_TRUE(login_custom_flags.size())
767 << "Error reading enum 'LoginCustomFlags' from histograms.xml.";
769 // Build reverse map {switch_name => id} from login_custom_flags.
770 SwitchToIdMap histograms_xml_switches_ids;
772 EXPECT_TRUE(login_custom_flags.count(testing::kBadSwitchFormatHistogramId))
773 << "Entry for UMA ID of incorrect command-line flag is not found in "
774 "histograms.xml enum LoginCustomFlags. "
775 "Consider adding entry:\n"
776 << " " << GetHistogramEnumEntryText("BAD_FLAG_FORMAT", 0);
777 // Check that all LoginCustomFlags entries have correct values.
778 for (const auto& entry : login_custom_flags) {
779 if (entry.first == testing::kBadSwitchFormatHistogramId) {
780 // Add error value with empty name.
781 SetSwitchToHistogramIdMapping(std::string(), entry.first,
782 &histograms_xml_switches_ids);
783 continue;
785 const Sample uma_id = GetSwitchUMAId(entry.second);
786 EXPECT_EQ(uma_id, entry.first)
787 << "histograms.xml enum LoginCustomFlags "
788 "entry '" << entry.second << "' has incorrect value=" << entry.first
789 << ", but " << uma_id << " is expected. Consider changing entry to:\n"
790 << " " << GetHistogramEnumEntryText(entry.second, uma_id);
791 SetSwitchToHistogramIdMapping(entry.second, entry.first,
792 &histograms_xml_switches_ids);
795 // Check that all flags in about_flags.cc have entries in login_custom_flags.
796 std::set<std::string> all_switches = GetAllSwitchesForTesting();
797 for (const std::string& flag : all_switches) {
798 // Skip empty placeholders.
799 if (flag.empty())
800 continue;
801 const Sample uma_id = GetSwitchUMAId(flag);
802 EXPECT_NE(testing::kBadSwitchFormatHistogramId, uma_id)
803 << "Command-line switch '" << flag
804 << "' from about_flags.cc has UMA ID equal to reserved value "
805 "kBadSwitchFormatHistogramId="
806 << testing::kBadSwitchFormatHistogramId
807 << ". Please modify switch name.";
808 SwitchToIdMap::iterator enum_entry =
809 histograms_xml_switches_ids.lower_bound(flag);
811 // Ignore case here when switch ID is incorrect - it has already been
812 // reported in the previous loop.
813 EXPECT_TRUE(enum_entry != histograms_xml_switches_ids.end() &&
814 enum_entry->first == flag)
815 << "histograms.xml enum LoginCustomFlags doesn't contain switch '"
816 << flag << "' (value=" << uma_id
817 << " expected). Consider adding entry:\n"
818 << " " << GetHistogramEnumEntryText(flag, uma_id);
822 } // namespace about_flags