| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/test/metrics/histogram_enum_reader.h" |
| |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/path_service.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/libxml/chromium/libxml_utils.h" |
| |
| namespace base { |
| namespace { |
| |
| // This is a helper function to the ReadEnumFromHistogramsXml(). |
| // Extracts single enum (with integer values) from histograms.xml. |
| // Expects |reader| to point at given enum. |
| // Returns map { value => label } on success, and nullopt on failure. |
| Optional<HistogramEnumEntryMap> ParseEnumFromHistogramsXml( |
| const std::string& enum_name, |
| XmlReader* reader) { |
| int entries_index = -1; |
| |
| HistogramEnumEntryMap result; |
| bool success = true; |
| |
| while (true) { |
| const std::string node_name = reader->NodeName(); |
| if (node_name == "enum" && reader->IsClosingElement()) |
| break; |
| |
| if (node_name == "int") { |
| ++entries_index; |
| std::string value_str; |
| std::string label; |
| const bool has_value = reader->NodeAttribute("value", &value_str); |
| const bool has_label = reader->NodeAttribute("label", &label); |
| if (!has_value) { |
| ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " |
| << entries_index << ", label='" << label |
| << "'): No 'value' attribute."; |
| success = false; |
| } |
| if (!has_label) { |
| ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " |
| << entries_index << ", value_str='" << value_str |
| << "'): No 'label' attribute."; |
| success = false; |
| } |
| |
| HistogramBase::Sample value; |
| if (has_value && !StringToInt(value_str, &value)) { |
| ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " |
| << entries_index << ", label='" << label |
| << "', value_str='" << value_str |
| << "'): 'value' attribute is not integer."; |
| success = false; |
| } |
| if (result.count(value)) { |
| ADD_FAILURE() << "Bad " << enum_name << " enum entry (at index " |
| << entries_index << ", label='" << label |
| << "', value_str='" << value_str |
| << "'): duplicate value '" << value_str |
| << "' found in enum. The previous one has label='" |
| << result[value] << "'."; |
| success = false; |
| } |
| if (success) |
| result[value] = label; |
| } |
| // All enum entries are on the same level, so it is enough to iterate |
| // until possible. |
| reader->Next(); |
| } |
| if (success) |
| return result; |
| return nullopt; |
| } |
| |
| } // namespace |
| |
| Optional<HistogramEnumEntryMap> ReadEnumFromEnumsXml( |
| const std::string& enum_name) { |
| FilePath src_root; |
| if (!PathService::Get(DIR_SOURCE_ROOT, &src_root)) { |
| ADD_FAILURE() << "Failed to get src root."; |
| return nullopt; |
| } |
| |
| base::FilePath enums_xml = src_root.AppendASCII("tools") |
| .AppendASCII("metrics") |
| .AppendASCII("histograms") |
| .AppendASCII("enums.xml"); |
| if (!PathExists(enums_xml)) { |
| ADD_FAILURE() << "enums.xml file does not exist."; |
| return nullopt; |
| } |
| |
| XmlReader enums_xml_reader; |
| if (!enums_xml_reader.LoadFile(enums_xml.MaybeAsASCII())) { |
| ADD_FAILURE() << "Failed to load enums.xml"; |
| return nullopt; |
| } |
| |
| Optional<HistogramEnumEntryMap> result; |
| |
| // Implement simple depth first search. |
| while (true) { |
| const std::string node_name = enums_xml_reader.NodeName(); |
| if (node_name == "enum") { |
| std::string name; |
| if (enums_xml_reader.NodeAttribute("name", &name) && name == enum_name) { |
| if (result.has_value()) { |
| ADD_FAILURE() << "Duplicate enum '" << enum_name |
| << "' found in enums.xml"; |
| return nullopt; |
| } |
| |
| const bool got_into_enum = enums_xml_reader.Read(); |
| if (!got_into_enum) { |
| ADD_FAILURE() << "Bad enum '" << enum_name |
| << "' (looks empty) found in enums.xml."; |
| return nullopt; |
| } |
| |
| result = ParseEnumFromHistogramsXml(enum_name, &enums_xml_reader); |
| if (!result.has_value()) { |
| ADD_FAILURE() << "Bad enum '" << enum_name |
| << "' found in histograms.xml (format error)."; |
| return nullopt; |
| } |
| } |
| } |
| // Go deeper if possible (stops at the closing tag of the deepest node). |
| if (enums_xml_reader.Read()) |
| continue; |
| |
| // Try next node on the same level (skips closing tag). |
| if (enums_xml_reader.Next()) |
| continue; |
| |
| // Go up until next node on the same level exists. |
| while (enums_xml_reader.Depth() && !enums_xml_reader.SkipToElement()) { |
| } |
| |
| // Reached top. histograms.xml consists of the single top level node |
| // 'histogram-configuration', so this is the end. |
| if (!enums_xml_reader.Depth()) |
| break; |
| } |
| return result; |
| } |
| |
| } // namespace base |