| OLD | NEW |
| 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 <stdint.h> | 5 #include <stdint.h> |
| 6 | 6 |
| 7 #include "base/files/file_path.h" | 7 #include "base/files/file_path.h" |
| 8 #include "base/memory/scoped_vector.h" | 8 #include "base/memory/scoped_vector.h" |
| 9 #include "base/path_service.h" | 9 #include "base/path_service.h" |
| 10 #include "base/prefs/pref_registry_simple.h" | 10 #include "base/prefs/pref_registry_simple.h" |
| (...skipping 23 matching lines...) Expand all Loading... |
| 34 const char kSwitch3[] = "switch3"; | 34 const char kSwitch3[] = "switch3"; |
| 35 const char kValueForSwitch2[] = "value_for_switch2"; | 35 const char kValueForSwitch2[] = "value_for_switch2"; |
| 36 | 36 |
| 37 const char kMultiSwitch1[] = "multi_switch1"; | 37 const char kMultiSwitch1[] = "multi_switch1"; |
| 38 const char kMultiSwitch2[] = "multi_switch2"; | 38 const char kMultiSwitch2[] = "multi_switch2"; |
| 39 const char kValueForMultiSwitch2[] = "value_for_multi_switch2"; | 39 const char kValueForMultiSwitch2[] = "value_for_multi_switch2"; |
| 40 | 40 |
| 41 const char kEnableDisableValue1[] = "value1"; | 41 const char kEnableDisableValue1[] = "value1"; |
| 42 const char kEnableDisableValue2[] = "value2"; | 42 const char kEnableDisableValue2[] = "value2"; |
| 43 | 43 |
| 44 typedef std::map<std::string, uint32_t> SwitchToIdMap; | 44 typedef base::HistogramBase::Sample Sample; |
| 45 typedef std::map<std::string, Sample> SwitchToIdMap; |
| 45 | 46 |
| 46 // This is a helper function to the ReadEnumFromHistogramsXml(). | 47 // This is a helper function to the ReadEnumFromHistogramsXml(). |
| 47 // Extracts single enum (with integer values) from histograms.xml. | 48 // Extracts single enum (with integer values) from histograms.xml. |
| 48 // Expects |reader| to point at given enum. | 49 // Expects |reader| to point at given enum. |
| 49 // Returns map { value => label }. | 50 // Returns map { value => label }. |
| 50 // Returns empty map on error. | 51 // Returns empty map on error. |
| 51 std::map<uint32_t, std::string> ParseEnumFromHistogramsXml( | 52 std::map<Sample, std::string> ParseEnumFromHistogramsXml( |
| 52 const std::string& enum_name, | 53 const std::string& enum_name, |
| 53 XmlReader* reader) { | 54 XmlReader* reader) { |
| 54 int entries_index = -1; | 55 int entries_index = -1; |
| 55 | 56 |
| 56 std::map<uint32_t, std::string> result; | 57 std::map<Sample, std::string> result; |
| 57 bool success = true; | 58 bool success = true; |
| 58 | 59 |
| 59 while (true) { | 60 while (true) { |
| 60 const std::string node_name = reader->NodeName(); | 61 const std::string node_name = reader->NodeName(); |
| 61 if (node_name == "enum" && reader->IsClosingElement()) | 62 if (node_name == "enum" && reader->IsClosingElement()) |
| 62 break; | 63 break; |
| 63 | 64 |
| 64 if (node_name == "int") { | 65 if (node_name == "int") { |
| 65 ++entries_index; | 66 ++entries_index; |
| 66 std::string value_str; | 67 std::string value_str; |
| 67 std::string label; | 68 std::string label; |
| 68 const bool has_value = reader->NodeAttribute("value", &value_str); | 69 const bool has_value = reader->NodeAttribute("value", &value_str); |
| 69 const bool has_label = reader->NodeAttribute("label", &label); | 70 const bool has_label = reader->NodeAttribute("label", &label); |
| 70 if (!has_value) { | 71 if (!has_value) { |
| 71 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " | 72 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " |
| 72 << entries_index << ", label='" << label | 73 << entries_index << ", label='" << label |
| 73 << "'): No 'value' attribute."; | 74 << "'): No 'value' attribute."; |
| 74 success = false; | 75 success = false; |
| 75 } | 76 } |
| 76 if (!has_label) { | 77 if (!has_label) { |
| 77 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " | 78 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " |
| 78 << entries_index << ", value_str='" << value_str | 79 << entries_index << ", value_str='" << value_str |
| 79 << "'): No 'label' attribute."; | 80 << "'): No 'label' attribute."; |
| 80 success = false; | 81 success = false; |
| 81 } | 82 } |
| 82 | 83 |
| 83 uint32_t value; | 84 Sample value; |
| 84 if (has_value && !base::StringToUint(value_str, &value)) { | 85 if (has_value && !base::StringToInt(value_str, &value)) { |
| 85 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " | 86 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " |
| 86 << entries_index << ", label='" << label | 87 << entries_index << ", label='" << label |
| 87 << "', value_str='" << value_str | 88 << "', value_str='" << value_str |
| 88 << "'): 'value' attribute is not integer."; | 89 << "'): 'value' attribute is not integer."; |
| 89 success = false; | 90 success = false; |
| 90 } | 91 } |
| 91 if (result.count(value)) { | 92 if (result.count(value)) { |
| 92 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " | 93 ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " |
| 93 << entries_index << ", label='" << label | 94 << entries_index << ", label='" << label |
| 94 << "', value_str='" << value_str | 95 << "', value_str='" << value_str |
| 95 << "'): duplicate value '" << value_str | 96 << "'): duplicate value '" << value_str |
| 96 << "' found in enum. The previous one has label='" | 97 << "' found in enum. The previous one has label='" |
| 97 << result[value] << "'."; | 98 << result[value] << "'."; |
| 98 success = false; | 99 success = false; |
| 99 } | 100 } |
| 100 if (success) { | 101 if (success) { |
| 101 result[value] = label; | 102 result[value] = label; |
| 102 } | 103 } |
| 103 } | 104 } |
| 104 // All enum entries are on the same level, so it is enough to iterate | 105 // All enum entries are on the same level, so it is enough to iterate |
| 105 // until possible. | 106 // until possible. |
| 106 reader->Next(); | 107 reader->Next(); |
| 107 } | 108 } |
| 108 return (success ? result : std::map<uint32_t, std::string>()); | 109 return (success ? result : std::map<Sample, std::string>()); |
| 109 } | 110 } |
| 110 | 111 |
| 111 // Find and read given enum (with integer values) from histograms.xml. | 112 // Find and read given enum (with integer values) from histograms.xml. |
| 112 // |enum_name| - enum name. | 113 // |enum_name| - enum name. |
| 113 // |histograms_xml| - must be loaded histograms.xml file. | 114 // |histograms_xml| - must be loaded histograms.xml file. |
| 114 // | 115 // |
| 115 // Returns map { value => label } so that: | 116 // Returns map { value => label } so that: |
| 116 // <int value="9" label="enable-pinch-virtual-viewport"/> | 117 // <int value="9" label="enable-pinch-virtual-viewport"/> |
| 117 // becomes: | 118 // becomes: |
| 118 // { 9 => "enable-pinch-virtual-viewport" } | 119 // { 9 => "enable-pinch-virtual-viewport" } |
| 119 // Returns empty map on error. | 120 // Returns empty map on error. |
| 120 std::map<uint32_t, std::string> ReadEnumFromHistogramsXml( | 121 std::map<Sample, std::string> ReadEnumFromHistogramsXml( |
| 121 const std::string& enum_name, | 122 const std::string& enum_name, |
| 122 XmlReader* histograms_xml) { | 123 XmlReader* histograms_xml) { |
| 123 std::map<uint32_t, std::string> login_custom_flags; | 124 std::map<Sample, std::string> login_custom_flags; |
| 124 | 125 |
| 125 // Implement simple depth first search. | 126 // Implement simple depth first search. |
| 126 while (true) { | 127 while (true) { |
| 127 const std::string node_name = histograms_xml->NodeName(); | 128 const std::string node_name = histograms_xml->NodeName(); |
| 128 if (node_name == "enum") { | 129 if (node_name == "enum") { |
| 129 std::string name; | 130 std::string name; |
| 130 if (histograms_xml->NodeAttribute("name", &name) && name == enum_name) { | 131 if (histograms_xml->NodeAttribute("name", &name) && name == enum_name) { |
| 131 if (!login_custom_flags.empty()) { | 132 if (!login_custom_flags.empty()) { |
| 132 EXPECT_TRUE(login_custom_flags.empty()) | 133 EXPECT_TRUE(login_custom_flags.empty()) |
| 133 << "Duplicate enum '" << enum_name << "' found in histograms.xml"; | 134 << "Duplicate enum '" << enum_name << "' found in histograms.xml"; |
| 134 return std::map<uint32_t, std::string>(); | 135 return std::map<Sample, std::string>(); |
| 135 } | 136 } |
| 136 | 137 |
| 137 const bool got_into_enum = histograms_xml->Read(); | 138 const bool got_into_enum = histograms_xml->Read(); |
| 138 if (got_into_enum) { | 139 if (got_into_enum) { |
| 139 login_custom_flags = | 140 login_custom_flags = |
| 140 ParseEnumFromHistogramsXml(enum_name, histograms_xml); | 141 ParseEnumFromHistogramsXml(enum_name, histograms_xml); |
| 141 EXPECT_FALSE(login_custom_flags.empty()) | 142 EXPECT_FALSE(login_custom_flags.empty()) |
| 142 << "Bad enum '" << enum_name | 143 << "Bad enum '" << enum_name |
| 143 << "' found in histograms.xml (format error)."; | 144 << "' found in histograms.xml (format error)."; |
| 144 } else { | 145 } else { |
| 145 EXPECT_TRUE(got_into_enum) | 146 EXPECT_TRUE(got_into_enum) |
| 146 << "Bad enum '" << enum_name | 147 << "Bad enum '" << enum_name |
| 147 << "' (looks empty) found in histograms.xml."; | 148 << "' (looks empty) found in histograms.xml."; |
| 148 } | 149 } |
| 149 if (login_custom_flags.empty()) | 150 if (login_custom_flags.empty()) |
| 150 return std::map<uint32_t, std::string>(); | 151 return std::map<Sample, std::string>(); |
| 151 } | 152 } |
| 152 } | 153 } |
| 153 // Go deeper if possible (stops at the closing tag of the deepest node). | 154 // Go deeper if possible (stops at the closing tag of the deepest node). |
| 154 if (histograms_xml->Read()) | 155 if (histograms_xml->Read()) |
| 155 continue; | 156 continue; |
| 156 | 157 |
| 157 // Try next node on the same level (skips closing tag). | 158 // Try next node on the same level (skips closing tag). |
| 158 if (histograms_xml->Next()) | 159 if (histograms_xml->Next()) |
| 159 continue; | 160 continue; |
| 160 | 161 |
| (...skipping 508 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 669 std::string name = experiments->internal_name; | 670 std::string name = experiments->internal_name; |
| 670 EXPECT_EQ(std::string::npos, name.find(testing::kMultiSeparator)) << i; | 671 EXPECT_EQ(std::string::npos, name.find(testing::kMultiSeparator)) << i; |
| 671 } | 672 } |
| 672 } | 673 } |
| 673 | 674 |
| 674 class AboutFlagsHistogramTest : public ::testing::Test { | 675 class AboutFlagsHistogramTest : public ::testing::Test { |
| 675 protected: | 676 protected: |
| 676 // This is a helper function to check that all IDs in enum LoginCustomFlags in | 677 // This is a helper function to check that all IDs in enum LoginCustomFlags in |
| 677 // histograms.xml are unique. | 678 // histograms.xml are unique. |
| 678 void SetSwitchToHistogramIdMapping(const std::string& switch_name, | 679 void SetSwitchToHistogramIdMapping(const std::string& switch_name, |
| 679 const uint32_t switch_histogram_id, | 680 const Sample switch_histogram_id, |
| 680 std::map<std::string, uint32_t>* out_map) { | 681 std::map<std::string, Sample>* out_map) { |
| 681 const std::pair<std::map<std::string, uint32_t>::iterator, bool> status = | 682 const std::pair<std::map<std::string, Sample>::iterator, bool> status = |
| 682 out_map->insert(std::make_pair(switch_name, switch_histogram_id)); | 683 out_map->insert(std::make_pair(switch_name, switch_histogram_id)); |
| 683 if (!status.second) { | 684 if (!status.second) { |
| 684 EXPECT_TRUE(status.first->second == switch_histogram_id) | 685 EXPECT_TRUE(status.first->second == switch_histogram_id) |
| 685 << "Duplicate switch '" << switch_name | 686 << "Duplicate switch '" << switch_name |
| 686 << "' found in enum 'LoginCustomFlags' in histograms.xml."; | 687 << "' found in enum 'LoginCustomFlags' in histograms.xml."; |
| 687 } | 688 } |
| 688 } | 689 } |
| 689 | 690 |
| 690 // This method generates a hint for the user for what string should be added | 691 // This method generates a hint for the user for what string should be added |
| 691 // to the enum LoginCustomFlags to make in consistent. | 692 // to the enum LoginCustomFlags to make in consistent. |
| 692 std::string GetHistogramEnumEntryText(const std::string& switch_name, | 693 std::string GetHistogramEnumEntryText(const std::string& switch_name, |
| 693 uint32_t value) { | 694 Sample value) { |
| 694 return base::StringPrintf( | 695 return base::StringPrintf( |
| 695 "<int value=\"%u\" label=\"%s\"/>", value, switch_name.c_str()); | 696 "<int value=\"%d\" label=\"%s\"/>", value, switch_name.c_str()); |
| 696 } | 697 } |
| 697 }; | 698 }; |
| 698 | 699 |
| 699 TEST_F(AboutFlagsHistogramTest, CheckHistograms) { | 700 TEST_F(AboutFlagsHistogramTest, CheckHistograms) { |
| 700 base::FilePath histograms_xml_file_path; | 701 base::FilePath histograms_xml_file_path; |
| 701 ASSERT_TRUE( | 702 ASSERT_TRUE( |
| 702 PathService::Get(base::DIR_SOURCE_ROOT, &histograms_xml_file_path)); | 703 PathService::Get(base::DIR_SOURCE_ROOT, &histograms_xml_file_path)); |
| 703 histograms_xml_file_path = histograms_xml_file_path.AppendASCII("tools") | 704 histograms_xml_file_path = histograms_xml_file_path.AppendASCII("tools") |
| 704 .AppendASCII("metrics") | 705 .AppendASCII("metrics") |
| 705 .AppendASCII("histograms") | 706 .AppendASCII("histograms") |
| 706 .AppendASCII("histograms.xml"); | 707 .AppendASCII("histograms.xml"); |
| 707 | 708 |
| 708 XmlReader histograms_xml; | 709 XmlReader histograms_xml; |
| 709 ASSERT_TRUE(histograms_xml.LoadFile( | 710 ASSERT_TRUE(histograms_xml.LoadFile( |
| 710 FilePathStringTypeToString(histograms_xml_file_path.value()))); | 711 FilePathStringTypeToString(histograms_xml_file_path.value()))); |
| 711 std::map<uint32_t, std::string> login_custom_flags = | 712 std::map<Sample, std::string> login_custom_flags = |
| 712 ReadEnumFromHistogramsXml("LoginCustomFlags", &histograms_xml); | 713 ReadEnumFromHistogramsXml("LoginCustomFlags", &histograms_xml); |
| 713 ASSERT_TRUE(login_custom_flags.size()) | 714 ASSERT_TRUE(login_custom_flags.size()) |
| 714 << "Error reading enum 'LoginCustomFlags' from histograms.xml."; | 715 << "Error reading enum 'LoginCustomFlags' from histograms.xml."; |
| 715 | 716 |
| 716 // Build reverse map {switch_name => id} from login_custom_flags. | 717 // Build reverse map {switch_name => id} from login_custom_flags. |
| 717 SwitchToIdMap histograms_xml_switches_ids; | 718 SwitchToIdMap histograms_xml_switches_ids; |
| 718 | 719 |
| 719 EXPECT_TRUE(login_custom_flags.count(kBadSwitchFormatHistogramId)) | 720 EXPECT_TRUE(login_custom_flags.count(kBadSwitchFormatHistogramId)) |
| 720 << "Entry for UMA ID of incorrect command-line flag is not found in " | 721 << "Entry for UMA ID of incorrect command-line flag is not found in " |
| 721 "histograms.xml enum LoginCustomFlags. " | 722 "histograms.xml enum LoginCustomFlags. " |
| 722 "Consider adding entry:\n" | 723 "Consider adding entry:\n" |
| 723 << " " << GetHistogramEnumEntryText("BAD_FLAG_FORMAT", 0); | 724 << " " << GetHistogramEnumEntryText("BAD_FLAG_FORMAT", 0); |
| 724 // Check that all LoginCustomFlags entries have correct values. | 725 // Check that all LoginCustomFlags entries have correct values. |
| 725 for (std::map<uint32_t, std::string>::const_iterator it = | 726 for (std::map<Sample, std::string>::const_iterator it = |
| 726 login_custom_flags.begin(); | 727 login_custom_flags.begin(); |
| 727 it != login_custom_flags.end(); | 728 it != login_custom_flags.end(); |
| 728 ++it) { | 729 ++it) { |
| 729 if (it->first == kBadSwitchFormatHistogramId) { | 730 if (it->first == kBadSwitchFormatHistogramId) { |
| 730 // Add eror value with empty name. | 731 // Add eror value with empty name. |
| 731 SetSwitchToHistogramIdMapping( | 732 SetSwitchToHistogramIdMapping( |
| 732 "", it->first, &histograms_xml_switches_ids); | 733 "", it->first, &histograms_xml_switches_ids); |
| 733 continue; | 734 continue; |
| 734 } | 735 } |
| 735 const uint32_t uma_id = GetSwitchUMAId(it->second); | 736 const Sample uma_id = GetSwitchUMAId(it->second); |
| 736 EXPECT_EQ(uma_id, it->first) | 737 EXPECT_EQ(uma_id, it->first) |
| 737 << "histograms.xml enum LoginCustomFlags " | 738 << "histograms.xml enum LoginCustomFlags " |
| 738 "entry '" << it->second << "' has incorrect value=" << it->first | 739 "entry '" << it->second << "' has incorrect value=" << it->first |
| 739 << ", but " << uma_id << " is expected. Consider changing entry to:\n" | 740 << ", but " << uma_id << " is expected. Consider changing entry to:\n" |
| 740 << " " << GetHistogramEnumEntryText(it->second, uma_id); | 741 << " " << GetHistogramEnumEntryText(it->second, uma_id); |
| 741 SetSwitchToHistogramIdMapping( | 742 SetSwitchToHistogramIdMapping( |
| 742 it->second, it->first, &histograms_xml_switches_ids); | 743 it->second, it->first, &histograms_xml_switches_ids); |
| 743 } | 744 } |
| 744 | 745 |
| 745 // Check that all flags in about_flags.cc have entries in login_custom_flags. | 746 // Check that all flags in about_flags.cc have entries in login_custom_flags. |
| 746 std::set<std::string> all_switches = GetAllSwitchesForTesting(); | 747 std::set<std::string> all_switches = GetAllSwitchesForTesting(); |
| 747 for (std::set<std::string>::const_iterator it = all_switches.begin(); | 748 for (std::set<std::string>::const_iterator it = all_switches.begin(); |
| 748 it != all_switches.end(); | 749 it != all_switches.end(); |
| 749 ++it) { | 750 ++it) { |
| 750 // Skip empty placeholders. | 751 // Skip empty placeholders. |
| 751 if (it->empty()) | 752 if (it->empty()) |
| 752 continue; | 753 continue; |
| 753 const uint32_t uma_id = GetSwitchUMAId(*it); | 754 const Sample uma_id = GetSwitchUMAId(*it); |
| 754 EXPECT_NE(kBadSwitchFormatHistogramId, uma_id) | 755 EXPECT_NE(kBadSwitchFormatHistogramId, uma_id) |
| 755 << "Command-line switch '" << *it | 756 << "Command-line switch '" << *it |
| 756 << "' from about_flags.cc has UMA ID equal to reserved value " | 757 << "' from about_flags.cc has UMA ID equal to reserved value " |
| 757 "kBadSwitchFormatHistogramId=" << kBadSwitchFormatHistogramId | 758 "kBadSwitchFormatHistogramId=" << kBadSwitchFormatHistogramId |
| 758 << ". Please modify switch name."; | 759 << ". Please modify switch name."; |
| 759 SwitchToIdMap::iterator enum_entry = | 760 SwitchToIdMap::iterator enum_entry = |
| 760 histograms_xml_switches_ids.lower_bound(*it); | 761 histograms_xml_switches_ids.lower_bound(*it); |
| 761 | 762 |
| 762 // Ignore case here when switch ID is incorrect - it has already been | 763 // Ignore case here when switch ID is incorrect - it has already been |
| 763 // reported in the previous loop. | 764 // reported in the previous loop. |
| 764 EXPECT_TRUE(enum_entry != histograms_xml_switches_ids.end() && | 765 EXPECT_TRUE(enum_entry != histograms_xml_switches_ids.end() && |
| 765 enum_entry->first == *it) | 766 enum_entry->first == *it) |
| 766 << "histograms.xml enum LoginCustomFlags doesn't contain switch '" | 767 << "histograms.xml enum LoginCustomFlags doesn't contain switch '" |
| 767 << *it << "' (value=" << uma_id | 768 << *it << "' (value=" << uma_id |
| 768 << " expected). Consider adding entry:\n" | 769 << " expected). Consider adding entry:\n" |
| 769 << " " << GetHistogramEnumEntryText(*it, uma_id); | 770 << " " << GetHistogramEnumEntryText(*it, uma_id); |
| 770 } | 771 } |
| 771 } | 772 } |
| 772 | 773 |
| 773 } // namespace about_flags | 774 } // namespace about_flags |
| OLD | NEW |