Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(104)

Side by Side Diff: chrome/browser/about_flags_unittest.cc

Issue 344883002: Collect UMA statistics on which chrome://flags lead to chrome restart on ChromeOS. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Report hash(switch name). Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/files/file_path.h"
6 #include "base/memory/scoped_vector.h"
7 #include "base/path_service.h"
5 #include "base/prefs/pref_registry_simple.h" 8 #include "base/prefs/pref_registry_simple.h"
6 #include "base/prefs/testing_pref_service.h" 9 #include "base/prefs/testing_pref_service.h"
7 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
8 #include "base/strings/utf_string_conversions.h" 12 #include "base/strings/utf_string_conversions.h"
9 #include "base/values.h" 13 #include "base/values.h"
10 #include "chrome/browser/about_flags.h" 14 #include "chrome/browser/about_flags.h"
11 #include "chrome/browser/pref_service_flags_storage.h" 15 #include "chrome/browser/pref_service_flags_storage.h"
12 #include "chrome/common/chrome_switches.h" 16 #include "chrome/common/chrome_switches.h"
13 #include "chrome/common/pref_names.h" 17 #include "chrome/common/pref_names.h"
14 #include "grit/chromium_strings.h" 18 #include "grit/chromium_strings.h"
15 #include "testing/gtest/include/gtest/gtest.h" 19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "third_party/libxml/chromium/libxml_utils.h"
21
22 namespace {
16 23
17 const char kFlags1[] = "flag1"; 24 const char kFlags1[] = "flag1";
18 const char kFlags2[] = "flag2"; 25 const char kFlags2[] = "flag2";
19 const char kFlags3[] = "flag3"; 26 const char kFlags3[] = "flag3";
20 const char kFlags4[] = "flag4"; 27 const char kFlags4[] = "flag4";
21 const char kFlags5[] = "flag5"; 28 const char kFlags5[] = "flag5";
22 29
23 const char kSwitch1[] = "switch"; 30 const char kSwitch1[] = "switch";
24 const char kSwitch2[] = "switch2"; 31 const char kSwitch2[] = "switch2";
25 const char kSwitch3[] = "switch3"; 32 const char kSwitch3[] = "switch3";
26 const char kValueForSwitch2[] = "value_for_switch2"; 33 const char kValueForSwitch2[] = "value_for_switch2";
27 34
28 const char kMultiSwitch1[] = "multi_switch1"; 35 const char kMultiSwitch1[] = "multi_switch1";
29 const char kMultiSwitch2[] = "multi_switch2"; 36 const char kMultiSwitch2[] = "multi_switch2";
30 const char kValueForMultiSwitch2[] = "value_for_multi_switch2"; 37 const char kValueForMultiSwitch2[] = "value_for_multi_switch2";
31 38
32 const char kEnableDisableValue1[] = "value1"; 39 const char kEnableDisableValue1[] = "value1";
33 const char kEnableDisableValue2[] = "value2"; 40 const char kEnableDisableValue2[] = "value2";
34 41
42 typedef std::map<std::string, uint32> Switch2IdMap;
43
44 // This is a helper function to the next one.
45 // Extracts single enum (with integer values) from histograms.xml.
46 // Expects |reader| to point at given enum.
47 // Returns map { value => label }.
48 // Returns empty map on error.
49 std::map<uint32, std::string> ParseEnumFromHistogramsXml(
50 const std::string& enum_name,
51 XmlReader* reader) {
52 int entries_index = -1;
53
54 std::map<uint32, std::string> result;
55 bool success = true;
56
57 while (true) {
58 const std::string node_name = reader->NodeName();
59 if (node_name == "enum" && reader->IsClosingElement())
60 break;
61
62 if (node_name == "int") {
63 ++entries_index;
64 std::string value_str;
65 std::string label;
66 const bool has_value = reader->NodeAttribute("value", &value_str);
67 const bool has_label = reader->NodeAttribute("label", &label);
68 if (!has_value) {
69 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
70 << entries_index << ", label='" << label
71 << "'): No 'value' attribute.";
72 success = false;
73 }
74 if (!has_label) {
75 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
76 << entries_index << ", value_str='" << value_str
77 << "'): No 'label' attribute.";
78 success = false;
79 }
80
81 uint32 value;
82 if (has_value && !base::StringToUint(value_str, &value)) {
83 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
84 << entries_index << ", label='" << label
85 << "', value_str='" << value_str
86 << "'): 'value' attribute is not integer.";
87 success = false;
88 }
89 if (result.count(value)) {
90 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
91 << entries_index << ", label='" << label
92 << "', value_str='" << value_str
93 << "'): duplicate value '" << value_str
94 << "' found in enum. The previous one has label='"
95 << result[value] << "'.";
96 success = false;
97 }
98 if (success) {
99 result[value] = label;
100 }
101 }
102 // All enum entries are on the same level, so it is enough to iterate
103 // until possible.
104 reader->Next();
105 }
106 return (success ? result : std::map<uint32, std::string>());
107 }
108
109 // Find and read given enum (with integer values) from histograms.xml.
110 // |enum_name| - enum name.
111 // |histograms_xml| - must be loaded histograms.xml file.
112 //
113 // Returns map { value => label } so that:
114 // <int value="9" label="enable-pinch-virtual-viewport"/>
115 // becomes:
116 // { 9 => "enable-pinch-virtual-viewport" }
117 // Returns empty map on error.
118 std::map<uint32, std::string> ReadEnumFromHistogramsXml(
119 const std::string& enum_name,
120 XmlReader* histograms_xml) {
121 std::map<uint32, std::string> login_custom_flags;
122
123 // Implement simple depth first search.
124 while (true) {
125 const std::string node_name = histograms_xml->NodeName();
126 if (node_name == "enum") {
127 std::string name;
128 if (histograms_xml->NodeAttribute("name", &name) && name == enum_name) {
129 if (!login_custom_flags.empty()) {
130 EXPECT_TRUE(login_custom_flags.empty())
131 << "Duplicate enum '" << enum_name << "' found in histograms.xml";
132 return std::map<uint32, std::string>();
133 }
134
135 const bool got_into_enum = histograms_xml->Read();
136 if (got_into_enum) {
137 login_custom_flags =
138 ParseEnumFromHistogramsXml(enum_name, histograms_xml);
139 EXPECT_FALSE(login_custom_flags.empty())
140 << "Bad enum '" << enum_name
141 << "' found in histograms.xml (format error).";
142 } else {
143 EXPECT_TRUE(got_into_enum)
144 << "Bad enum '" << enum_name
145 << "' (looks empty) found in histograms.xml.";
146 }
147 if (login_custom_flags.empty())
148 return std::map<uint32, std::string>();
149 }
150 }
151 // Go deeper if possible (stops at the closing tag of the deepest node).
152 if (histograms_xml->Read())
153 continue;
154
155 // Try next node on the same level (skips closing tag).
156 if (histograms_xml->Next())
157 continue;
158
159 // Go up until next node on the same level exists.
160 while (histograms_xml->Depth() && !histograms_xml->SkipToElement()) {
161 }
162
163 // Reached top. histograms.xml consists of the single top level node
164 // 'histogram-configuration', so this is the end.
165 if (!histograms_xml->Depth())
166 break;
167 }
168 EXPECT_FALSE(login_custom_flags.empty())
169 << "Enum '" << enum_name << "' is not found in histograms.xml.";
170 return login_custom_flags;
171 }
172
173 std::string FilePathStringTypeToString(const base::FilePath::StringType& path) {
174 #if defined(OS_WIN)
175 return UTF16ToUTF8(path);
176 #else
177 return path;
178 #endif
179 }
180
181 } // anonymous namespace
182
35 namespace about_flags { 183 namespace about_flags {
36 184
37 const Experiment::Choice kMultiChoices[] = { 185 const Experiment::Choice kMultiChoices[] = {
38 { IDS_PRODUCT_NAME, "", "" }, 186 { IDS_PRODUCT_NAME, "", "" },
39 { IDS_PRODUCT_NAME, kMultiSwitch1, "" }, 187 { IDS_PRODUCT_NAME, kMultiSwitch1, "" },
40 { IDS_PRODUCT_NAME, kMultiSwitch2, kValueForMultiSwitch2 }, 188 { IDS_PRODUCT_NAME, kMultiSwitch2, kValueForMultiSwitch2 },
41 }; 189 };
42 190
43 // The experiments that are set for these tests. The 3rd experiment is not 191 // The experiments that are set for these tests. The 3rd experiment is not
44 // supported on the current platform, all others are. 192 // supported on the current platform, all others are.
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 }; 259 };
112 260
113 class AboutFlagsTest : public ::testing::Test { 261 class AboutFlagsTest : public ::testing::Test {
114 protected: 262 protected:
115 AboutFlagsTest() : flags_storage_(&prefs_) { 263 AboutFlagsTest() : flags_storage_(&prefs_) {
116 prefs_.registry()->RegisterListPref(prefs::kEnabledLabsExperiments); 264 prefs_.registry()->RegisterListPref(prefs::kEnabledLabsExperiments);
117 testing::ClearState(); 265 testing::ClearState();
118 } 266 }
119 267
120 virtual void SetUp() OVERRIDE { 268 virtual void SetUp() OVERRIDE {
269 std::set<std::string> all_switches = GetAllSwitchesForTesting();
121 for (size_t i = 0; i < arraysize(kExperiments); ++i) 270 for (size_t i = 0; i < arraysize(kExperiments); ++i)
122 kExperiments[i].supported_platforms = GetCurrentPlatform(); 271 kExperiments[i].supported_platforms = GetCurrentPlatform();
123 272
124 int os_other_than_current = 1; 273 int os_other_than_current = 1;
125 while (os_other_than_current == GetCurrentPlatform()) 274 while (os_other_than_current == GetCurrentPlatform())
126 os_other_than_current <<= 1; 275 os_other_than_current <<= 1;
127 kExperiments[2].supported_platforms = os_other_than_current; 276 kExperiments[2].supported_platforms = os_other_than_current;
128 277
129 testing::SetExperiments(kExperiments, arraysize(kExperiments)); 278 testing::SetExperiments(kExperiments, arraysize(kExperiments));
130 } 279 }
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 375
227 CommandLine command_line2(CommandLine::NO_PROGRAM); 376 CommandLine command_line2(CommandLine::NO_PROGRAM);
228 377
229 ConvertFlagsToSwitches(&flags_storage_, &command_line2, kNoSentinels); 378 ConvertFlagsToSwitches(&flags_storage_, &command_line2, kNoSentinels);
230 379
231 EXPECT_TRUE(command_line2.HasSwitch(kSwitch1)); 380 EXPECT_TRUE(command_line2.HasSwitch(kSwitch1));
232 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesBegin)); 381 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesBegin));
233 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesEnd)); 382 EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesEnd));
234 } 383 }
235 384
385 CommandLine::StringType CreateSwitch(const std::string& value) {
386 #if defined(OS_WIN)
387 return ASCIIToUTF16(value);
388 #else
389 return value;
390 #endif
391 }
392
236 TEST_F(AboutFlagsTest, CompareSwitchesToCurrentCommandLine) { 393 TEST_F(AboutFlagsTest, CompareSwitchesToCurrentCommandLine) {
237 SetExperimentEnabled(&flags_storage_, kFlags1, true); 394 SetExperimentEnabled(&flags_storage_, kFlags1, true);
238 395
396 const std::string kDoubleDash("--");
397
239 CommandLine command_line(CommandLine::NO_PROGRAM); 398 CommandLine command_line(CommandLine::NO_PROGRAM);
240 command_line.AppendSwitch("foo"); 399 command_line.AppendSwitch("foo");
241 400
242 CommandLine new_command_line(CommandLine::NO_PROGRAM); 401 CommandLine new_command_line(CommandLine::NO_PROGRAM);
243 ConvertFlagsToSwitches(&flags_storage_, &new_command_line, kAddSentinels); 402 ConvertFlagsToSwitches(&flags_storage_, &new_command_line, kAddSentinels);
244 403
245 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(new_command_line, 404 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
246 command_line)); 405 new_command_line, command_line, NULL));
406 {
407 std::set<CommandLine::StringType> difference;
408 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
409 new_command_line, command_line, &difference));
410 EXPECT_EQ(1U, difference.size());
411 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch1)));
412 }
247 413
248 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels); 414 ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
249 415
250 EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(new_command_line, 416 EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(
251 command_line)); 417 new_command_line, command_line, NULL));
418 {
419 std::set<CommandLine::StringType> difference;
420 EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(
421 new_command_line, command_line, &difference));
422 EXPECT_TRUE(difference.empty());
423 }
252 424
253 // Now both have flags but different. 425 // Now both have flags but different.
254 SetExperimentEnabled(&flags_storage_, kFlags1, false); 426 SetExperimentEnabled(&flags_storage_, kFlags1, false);
255 SetExperimentEnabled(&flags_storage_, kFlags2, true); 427 SetExperimentEnabled(&flags_storage_, kFlags2, true);
256 428
257 CommandLine another_command_line(CommandLine::NO_PROGRAM); 429 CommandLine another_command_line(CommandLine::NO_PROGRAM);
258 ConvertFlagsToSwitches(&flags_storage_, &another_command_line, kAddSentinels); 430 ConvertFlagsToSwitches(&flags_storage_, &another_command_line, kAddSentinels);
259 431
260 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(new_command_line, 432 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
261 another_command_line)); 433 new_command_line, another_command_line, NULL));
434 {
435 std::set<CommandLine::StringType> difference;
436 EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(
437 new_command_line, another_command_line, &difference));
438 EXPECT_EQ(2U, difference.size());
439 EXPECT_EQ(1U, difference.count(CreateSwitch(kDoubleDash + kSwitch1)));
440 EXPECT_EQ(1U,
441 difference.count(CreateSwitch(kDoubleDash + kSwitch2 + "=" +
442 kValueForSwitch2)));
443 }
262 } 444 }
263 445
264 TEST_F(AboutFlagsTest, RemoveFlagSwitches) { 446 TEST_F(AboutFlagsTest, RemoveFlagSwitches) {
265 std::map<std::string, CommandLine::StringType> switch_list; 447 std::map<std::string, CommandLine::StringType> switch_list;
266 switch_list[kSwitch1] = CommandLine::StringType(); 448 switch_list[kSwitch1] = CommandLine::StringType();
267 switch_list[switches::kFlagSwitchesBegin] = CommandLine::StringType(); 449 switch_list[switches::kFlagSwitchesBegin] = CommandLine::StringType();
268 switch_list[switches::kFlagSwitchesEnd] = CommandLine::StringType(); 450 switch_list[switches::kFlagSwitchesEnd] = CommandLine::StringType();
269 switch_list["foo"] = CommandLine::StringType(); 451 switch_list["foo"] = CommandLine::StringType();
270 452
271 SetExperimentEnabled(&flags_storage_, kFlags1, true); 453 SetExperimentEnabled(&flags_storage_, kFlags1, true);
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
457 TEST_F(AboutFlagsTest, NoSeparators) { 639 TEST_F(AboutFlagsTest, NoSeparators) {
458 testing::SetExperiments(NULL, 0); 640 testing::SetExperiments(NULL, 0);
459 size_t count; 641 size_t count;
460 const Experiment* experiments = testing::GetExperiments(&count); 642 const Experiment* experiments = testing::GetExperiments(&count);
461 for (size_t i = 0; i < count; ++i) { 643 for (size_t i = 0; i < count; ++i) {
462 std::string name = experiments->internal_name; 644 std::string name = experiments->internal_name;
463 EXPECT_EQ(std::string::npos, name.find(testing::kMultiSeparator)) << i; 645 EXPECT_EQ(std::string::npos, name.find(testing::kMultiSeparator)) << i;
464 } 646 }
465 } 647 }
466 648
649 class AboutFlagsHistogramTest : public ::testing::Test {
650 protected:
651 void SetSwitchToHistogramIdMapping(const std::string& switch_name,
652 const uint32 switch_histogram_id,
653 std::map<std::string, uint32>* out_map) {
654 const std::pair<std::map<std::string, uint32>::iterator, bool> status =
655 out_map->insert(std::make_pair(switch_name, switch_histogram_id));
656 if (!status.second) {
657 EXPECT_TRUE(status.first->second == switch_histogram_id)
658 << "Duplicate switch '" << switch_name
659 << "' found in enum 'LoginCustomFlags' in histograms.xml.";
660 }
661 }
662
663 std::string GetHistogramEnumEntryText(const std::string& switch_name,
664 uint32 value) {
665 return base::StringPrintf(
666 "<int value=\"%u\" label=\"%s\"/>", value, switch_name.c_str());
667 }
668 };
669
670 TEST_F(AboutFlagsHistogramTest, CheckHistograms) {
671 base::FilePath histograms_xml_file_path;
672 ASSERT_TRUE(
673 PathService::Get(base::DIR_SOURCE_ROOT, &histograms_xml_file_path));
674 histograms_xml_file_path = histograms_xml_file_path.AppendASCII("tools")
675 .AppendASCII("metrics")
676 .AppendASCII("histograms")
677 .AppendASCII("histograms.xml");
678
679 XmlReader histograms_xml;
680 ASSERT_TRUE(histograms_xml.LoadFile(
681 FilePathStringTypeToString(histograms_xml_file_path.value())));
682 std::map<uint32, std::string> login_custom_flags =
683 ReadEnumFromHistogramsXml("LoginCustomFlags", &histograms_xml);
684 ASSERT_TRUE(login_custom_flags.size())
685 << "Error reading enum 'LoginCustomFlags' from histograms.xml.";
686
687 // Build reverse map {switch_name => id} from login_custom_flags.
688 Switch2IdMap histograms_xml_switches_ids;
689
690 EXPECT_TRUE(login_custom_flags.count(kBadSwitchFormatHistogramId))
691 << "Entry for UMA ID of incorrect command-line flag is not found in "
692 "histograms.xml enum LoginCustomFlags. "
693 "Consider adding entry:\n"
694 << " " << GetHistogramEnumEntryText("BAD_FLAG_FORMAT", 0);
695 // Check that all LoginCustomFlags entries have correct values.
696 for (std::map<uint32, std::string>::const_iterator it =
697 login_custom_flags.begin();
698 it != login_custom_flags.end();
699 ++it) {
700 if (it->first == kBadSwitchFormatHistogramId) {
701 // Add eror value with empty name.
702 SetSwitchToHistogramIdMapping(
703 "", it->first, &histograms_xml_switches_ids);
704 continue;
705 }
706 const uint32 uma_id = GetSwitchUMAId(it->second);
707 EXPECT_EQ(uma_id, it->first)
708 << "histograms.xml enum LoginCustomFlags "
709 "entry '" << it->second << "' has incorrect value=" << it->first
710 << ", but " << uma_id << " is expected. Consider changing entry to:\n"
711 << " " << GetHistogramEnumEntryText(it->second, uma_id);
712 SetSwitchToHistogramIdMapping(
713 it->second, it->first, &histograms_xml_switches_ids);
714 }
715
716 // Check that all flags in about_flags.cc have entries in login_custom_flags.
717 std::set<std::string> all_switches = GetAllSwitchesForTesting();
718 for (std::set<std::string>::const_iterator it = all_switches.begin();
719 it != all_switches.end();
720 ++it) {
721 // Skip empty placeholders.
722 if (it->empty())
723 continue;
724 const uint32 uma_id = GetSwitchUMAId(*it);
725 EXPECT_NE(kBadSwitchFormatHistogramId, uma_id)
726 << "Command-line switch '" << *it
727 << "' from about_flags.cc has UMA ID equal to reserved value "
728 "kBadSwitchFormatHistogramId=" << kBadSwitchFormatHistogramId
729 << ". Please modify switch name.";
730 Switch2IdMap::iterator enum_entry =
731 histograms_xml_switches_ids.lower_bound(*it);
732
733 // Ignore case here when switch ID is incorrect - it has already been
734 // reported in the previous loop.
735 EXPECT_TRUE(enum_entry != histograms_xml_switches_ids.end() &&
736 enum_entry->first == *it)
737 << "histograms.xml enum LoginCustomFlags doesn't contain switch '"
738 << *it << "' (value=" << uma_id
739 << " expected). Consider adding entry:\n"
740 << " " << GetHistogramEnumEntryText(*it, uma_id);
741 }
742 }
743
467 } // namespace about_flags 744 } // namespace about_flags
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698