// 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