histogram_enum_reader.cc 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // Copyright 2018 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.
  4. #include "base/test/metrics/histogram_enum_reader.h"
  5. #include "base/files/file_path.h"
  6. #include "base/files/file_util.h"
  7. #include "base/path_service.h"
  8. #include "base/strings/string_number_conversions.h"
  9. #include "testing/gtest/include/gtest/gtest.h"
  10. #include "third_party/abseil-cpp/absl/types/optional.h"
  11. #include "third_party/libxml/chromium/xml_reader.h"
  12. namespace base {
  13. namespace {
  14. // This is a helper function to the ReadEnumFromHistogramsXml().
  15. // Extracts single enum (with integer values) from histograms.xml.
  16. // Expects |reader| to point at given enum.
  17. // Returns map { value => label } on success, and nullopt on failure.
  18. absl::optional<HistogramEnumEntryMap> ParseEnumFromHistogramsXml(
  19. const std::string& enum_name,
  20. XmlReader* reader) {
  21. int entries_index = -1;
  22. HistogramEnumEntryMap result;
  23. bool success = true;
  24. while (true) {
  25. const std::string node_name = reader->NodeName();
  26. if (node_name == "enum" && reader->IsClosingElement())
  27. break;
  28. if (node_name == "int") {
  29. ++entries_index;
  30. std::string value_str;
  31. std::string label;
  32. const bool has_value = reader->NodeAttribute("value", &value_str);
  33. const bool has_label = reader->NodeAttribute("label", &label);
  34. if (!has_value) {
  35. ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
  36. << entries_index << ", label='" << label
  37. << "'): No 'value' attribute.";
  38. success = false;
  39. }
  40. if (!has_label) {
  41. ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
  42. << entries_index << ", value_str='" << value_str
  43. << "'): No 'label' attribute.";
  44. success = false;
  45. }
  46. HistogramBase::Sample value;
  47. if (has_value && !StringToInt(value_str, &value)) {
  48. ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
  49. << entries_index << ", label='" << label
  50. << "', value_str='" << value_str
  51. << "'): 'value' attribute is not integer.";
  52. success = false;
  53. }
  54. if (result.count(value)) {
  55. ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index "
  56. << entries_index << ", label='" << label
  57. << "', value_str='" << value_str
  58. << "'): duplicate value '" << value_str
  59. << "' found in enum. The previous one has label='"
  60. << result[value] << "'.";
  61. success = false;
  62. }
  63. if (success)
  64. result[value] = label;
  65. }
  66. // All enum entries are on the same level, so it is enough to iterate
  67. // until possible.
  68. reader->Next();
  69. }
  70. if (success)
  71. return result;
  72. return absl::nullopt;
  73. }
  74. } // namespace
  75. absl::optional<HistogramEnumEntryMap> ReadEnumFromEnumsXml(
  76. const std::string& enum_name) {
  77. FilePath src_root;
  78. if (!PathService::Get(DIR_SOURCE_ROOT, &src_root)) {
  79. ADD_FAILURE() << "Failed to get src root.";
  80. return absl::nullopt;
  81. }
  82. base::FilePath enums_xml = src_root.AppendASCII("tools")
  83. .AppendASCII("metrics")
  84. .AppendASCII("histograms")
  85. .AppendASCII("enums.xml");
  86. if (!PathExists(enums_xml)) {
  87. ADD_FAILURE() << "enums.xml file does not exist.";
  88. return absl::nullopt;
  89. }
  90. XmlReader enums_xml_reader;
  91. if (!enums_xml_reader.LoadFile(enums_xml.MaybeAsASCII())) {
  92. ADD_FAILURE() << "Failed to load enums.xml";
  93. return absl::nullopt;
  94. }
  95. absl::optional<HistogramEnumEntryMap> result;
  96. // Implement simple depth first search.
  97. while (true) {
  98. const std::string node_name = enums_xml_reader.NodeName();
  99. if (node_name == "enum") {
  100. std::string name;
  101. if (enums_xml_reader.NodeAttribute("name", &name) && name == enum_name) {
  102. if (result.has_value()) {
  103. ADD_FAILURE() << "Duplicate enum '" << enum_name
  104. << "' found in enums.xml";
  105. return absl::nullopt;
  106. }
  107. const bool got_into_enum = enums_xml_reader.Read();
  108. if (!got_into_enum) {
  109. ADD_FAILURE() << "Bad enum '" << enum_name
  110. << "' (looks empty) found in enums.xml.";
  111. return absl::nullopt;
  112. }
  113. result = ParseEnumFromHistogramsXml(enum_name, &enums_xml_reader);
  114. if (!result.has_value()) {
  115. ADD_FAILURE() << "Bad enum '" << enum_name
  116. << "' found in histograms.xml (format error).";
  117. return absl::nullopt;
  118. }
  119. }
  120. }
  121. // Go deeper if possible (stops at the closing tag of the deepest node).
  122. if (enums_xml_reader.Read())
  123. continue;
  124. // Try next node on the same level (skips closing tag).
  125. if (enums_xml_reader.Next())
  126. continue;
  127. // Go up until next node on the same level exists.
  128. while (enums_xml_reader.Depth() && !enums_xml_reader.SkipToElement()) {
  129. }
  130. // Reached top. histograms.xml consists of the single top level node
  131. // 'histogram-configuration', so this is the end.
  132. if (!enums_xml_reader.Depth())
  133. break;
  134. }
  135. return result;
  136. }
  137. } // namespace base