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