Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: build/android/emma_coverage_stats.py

Issue 1211243016: Added coverage script and tests. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Removed references to lxml and added test to presubmit. Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « build/android/PRESUBMIT.py ('k') | build/android/emma_coverage_stats_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright 2015 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Generates incremental code coverage reports for Java code in Chromium."""
7
8 import collections
9 import os
10 from xml.etree import ElementTree
11
12 NOT_EXECUTABLE = -1
13 NOT_COVERED = 0
14 COVERED = 1
15 PARTIALLY_COVERED = 2
16
17 # Coverage information about a single line of code.
18 LineCoverage = collections.namedtuple(
19 'LineCoverage',
20 ['lineno', 'source', 'covered_status', 'fractional_line_coverage'])
21
22
23 class _EmmaHtmlParser(object):
24 """Encapsulates HTML file parsing operations.
25
26 This class contains all operations related to parsing HTML files that were
27 produced using the EMMA code coverage tool.
28
29 Example HTML:
30
31 Package links:
32 <a href="_files/1.html">org.chromium.chrome</a>
33 This is returned by the selector |XPATH_SELECT_PACKAGE_ELEMENTS|.
34
35 Class links:
36 <a href="1e.html">DoActivity.java</a>
37 This is returned by the selector |XPATH_SELECT_CLASS_ELEMENTS|.
38
39 Line coverage data:
40 <tr class="p">
41 <td class="l" title="78% line coverage (7 out of 9)">108</td>
42 <td title="78% line coverage (7 out of 9 instructions)">
43 if (index < 0 || index = mSelectors.size()) index = 0;</td>
44 </tr>
45 <tr>
46 <td class="l">109</td>
47 <td> </td>
48 </tr>
49 <tr class="c">
50 <td class="l">110</td>
51 <td> if (mSelectors.get(index) != null) {</td>
52 </tr>
53 <tr class="z">
54 <td class="l">111</td>
55 <td> for (int i = 0; i < mSelectors.size(); i++) {</td>
56 </tr>
57 Each <tr> element is returned by the selector |XPATH_SELECT_LOC|.
58
59 We can parse this to get:
60 1. Line number
61 2. Line of source code
62 3. Coverage status (c, z, or p)
63 4. Fractional coverage value (% out of 100 if PARTIALLY_COVERED)
64 """
65 # Selector to match all <a> elements within the rows that are in the table
66 # that displays all of the different packages.
67 _XPATH_SELECT_PACKAGE_ELEMENTS = './/BODY/TABLE[4]/TR/TD/A'
68
69 # Selector to match all <a> elements within the rows that are in the table
70 # that displays all of the different packages within a class.
71 _XPATH_SELECT_CLASS_ELEMENTS = './/BODY/TABLE[3]/TR/TD/A'
72
73 # Selector to match all <tr> elements within the table containing Java source
74 # code in an EMMA HTML file.
75 _XPATH_SELECT_LOC = './/BODY/TABLE[4]/TR'
76
77 # Children of HTML elements are represented as a list in ElementTree. These
78 # constants represent list indices corresponding to relevant child elements.
79
80 # Child 1 contains percentage covered for a line.
81 _ELEMENT_PERCENT_COVERED = 1
82
83 # Child 1 contains the original line of source code.
84 _ELEMENT_CONTAINING_SOURCE_CODE = 1
85
86 # Child 0 contains the line number.
87 _ELEMENT_CONTAINING_LINENO = 0
88
89 # Maps CSS class names to corresponding coverage constants.
90 _CSS_TO_STATUS = {'c': COVERED, 'p': PARTIALLY_COVERED, 'z': NOT_COVERED}
91
92 # UTF-8 no break space.
93 _NO_BREAK_SPACE = '\xc2\xa0'
94
95 def __init__(self, emma_file_base_dir):
96 """Initializes _EmmaHtmlParser.
97
98 Args:
99 emma_file_base_dir: Path to the location where EMMA report files are
100 stored. Should be where index.html is stored.
101 """
102 self._base_dir = emma_file_base_dir
103 self._emma_files_path = os.path.join(self._base_dir, '_files')
104 self._index_path = os.path.join(self._base_dir, 'index.html')
105
106 def GetLineCoverage(self, emma_file_path):
107 """Returns a list of LineCoverage objects for the given EMMA HTML file.
108
109 Args:
110 emma_file_path: String representing the path to the EMMA HTML file.
111
112 Returns:
113 A list of LineCoverage objects.
114 """
115 line_tr_elements = self._FindElements(
116 emma_file_path, self._XPATH_SELECT_LOC)
117 line_coverage = []
118 for tr in line_tr_elements:
119 # Get the coverage status.
120 coverage_status = self._CSS_TO_STATUS.get(tr.get('CLASS'), NOT_EXECUTABLE)
121 # Get the fractional coverage value.
122 if coverage_status == PARTIALLY_COVERED:
123 title_attribute = (tr[self._ELEMENT_PERCENT_COVERED].get('TITLE'))
124 # Parse string that contains percent covered: "83% line coverage ...".
125 percent_covered = title_attribute.split('%')[0]
126 fractional_coverage = int(percent_covered) / 100.0
127 else:
128 fractional_coverage = 1.0
129
130 # Get the line number.
131 lineno_element = tr[self._ELEMENT_CONTAINING_LINENO]
132 # Handles oddly formatted HTML (where there is an extra <a> tag).
133 lineno = int(lineno_element.text or
134 lineno_element[self._ELEMENT_CONTAINING_LINENO].text)
135 # Get the original line of Java source code.
136 raw_source = tr[self._ELEMENT_CONTAINING_SOURCE_CODE].text
137 utf8_source = raw_source.encode('UTF-8')
138 source = utf8_source.replace(self._NO_BREAK_SPACE, ' ')
139
140 line = LineCoverage(lineno, source, coverage_status, fractional_coverage)
141 line_coverage.append(line)
142
143 return line_coverage
144
145 def GetPackageNameToEmmaFileDict(self):
146 """Returns a dict mapping Java packages to EMMA HTML coverage files.
147
148 Parses the EMMA index.html file to get a list of packages, then parses each
149 package HTML file to get a list of classes for that package, and creates
150 a dict with this info.
151
152 Returns:
153 A dict mapping string representation of Java packages (with class
154 names appended) to the corresponding file paths of EMMA HTML files.
155 """
156 # These <a> elements contain each package name and the path of the file
157 # where all classes within said package are listed.
158 package_link_elements = self._FindElements(
159 self._index_path, self._XPATH_SELECT_PACKAGE_ELEMENTS)
160 # Maps file path of package directory (EMMA generated) to package name.
161 # Ex. emma_dir/f.html: org.chromium.chrome.
162 package_links = {
163 os.path.join(self._base_dir, link.attrib['HREF']): link.text
164 for link in package_link_elements if 'HREF' in link.attrib
165 }
166
167 package_to_emma = {}
168 for package_emma_file_path, package_name in package_links.iteritems():
169 # These <a> elements contain each class name in the current package and
170 # the path of the file where the coverage info is stored for each class.
171 coverage_file_link_elements = self._FindElements(
172 package_emma_file_path, self._XPATH_SELECT_CLASS_ELEMENTS)
173
174 for coverage_file_element in coverage_file_link_elements:
175 emma_coverage_file_path = os.path.join(
176 self._emma_files_path, coverage_file_element.attrib['HREF'])
177 full_package_name = '%s.%s' % (package_name, coverage_file_element.text)
178 package_to_emma[full_package_name] = emma_coverage_file_path
179
180 return package_to_emma
181
182 def _FindElements(self, file_path, xpath_selector):
183 """Reads a HTML file and performs an XPath match.
184
185 Args:
186 file_path: String representing the path to the HTML file.
187 xpath_selector: String representing xpath search pattern.
188
189 Returns:
190 A list of ElementTree.Elements matching the given XPath selector.
191 Returns an empty list if there is no match.
192 """
193 with open(file_path) as f:
194 file_contents = f.read().decode('ISO-8859-1').encode('UTF-8')
195 root = ElementTree.fromstring(file_contents)
196 return root.findall(xpath_selector)
OLDNEW
« no previous file with comments | « build/android/PRESUBMIT.py ('k') | build/android/emma_coverage_stats_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698