OLD | NEW |
---|---|
(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 from lxml import etree | |
7 from lxml import html | |
8 import os | |
9 import sys | |
10 import unittest | |
11 | |
12 import coverage | |
13 from pylib import constants | |
14 | |
15 sys.path.append(os.path.join( | |
16 constants.DIR_SOURCE_ROOT, 'third_party', 'pymock')) | |
17 import mock # pylint: disable=F0401 | |
18 | |
19 | |
20 class LineCoverageTest(unittest.TestCase): | |
21 """Tests for LineCoverage.""" | |
22 | |
23 def testInit(self): | |
24 line_coverage = coverage.LineCoverage(10, 'i++;', | |
25 coverage.LineCoverage.COVERED, 1.0) | |
26 self.assertEqual(line_coverage.lineno, 10) | |
27 self.assertEqual(line_coverage.source, 'i++;') | |
28 self.assertEqual(line_coverage.covered_status, | |
29 coverage.LineCoverage.COVERED) | |
30 self.assertEqual(line_coverage.fractional_line_coverage, 1.0) | |
31 | |
32 | |
33 class _EmmaHtmlParserTest(unittest.TestCase): | |
34 """Tests for _EmmaHtmlParser. | |
35 | |
36 Uses modified EMMA report HTML that contains only the subset of tags needed | |
37 for test verification. | |
38 """ | |
39 | |
40 def setUp(self): | |
41 self.emma_dir = 'fake/dir/' | |
42 self.parser = coverage._EmmaHtmlParser(self.emma_dir) | |
43 self.simple_html = '<td class="p">Test HTML</td>' | |
44 self.index_html = """ | |
45 <HTML> | |
46 <BODY> | |
47 <TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%"> | |
48 </TABLE> | |
49 <TABLE CELLSPACING="0" WIDTH="100%"> | |
50 </TABLE> | |
51 <TABLE CLASS="it" CELLSPACING="0"> | |
52 </TABLE> | |
53 <TABLE CELLSPACING="0" WIDTH="100%"> | |
54 <TR> | |
55 <TH CLASS="f">name</TH> | |
56 <TH>class, %</TH> | |
57 <TH>method, %</TH> | |
58 <TH>block, %</TH> | |
59 <TH>line, %</TH> | |
60 </TR> | |
61 <TR CLASS="o"> | |
62 <TD><A HREF="_files/0.html" | |
63 >org.chromium.chrome.browser</A></TD> | |
64 <TD CLASS="h">0% (0/3)</TD> | |
65 </TR> | |
66 <TR> | |
67 <TD><A HREF="_files/1.html" | |
68 >org.chromium.chrome.browser.tabmodel</A></TD> | |
69 <TD CLASS="h">0% (0/8)</TD> | |
70 </TR> | |
71 </TABLE> | |
72 <TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%"> | |
73 </TABLE> | |
74 </BODY> | |
75 </HTML>""" | |
76 self.package_1_class_list_html = """ | |
77 <HTML> | |
78 <BODY> | |
79 <TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%"> | |
80 </TABLE> | |
81 <TABLE CELLSPACING="0" WIDTH="100%"> | |
82 </TABLE> | |
83 <TABLE CELLSPACING="0" WIDTH="100%"> | |
84 <TR> | |
85 <TH CLASS="f">name</TH> | |
86 <TH>class, %</TH> | |
87 <TH>method, %</TH> | |
88 <TH>block, %</TH> | |
89 <TH>line, %</TH> | |
90 </TR> | |
91 <TR CLASS="o"> | |
92 <TD><A HREF="1e.html">IntentHelper.java</A></TD> | |
93 <TD CLASS="h">0% (0/3)</TD> | |
94 <TD CLASS="h">0% (0/9)</TD> | |
95 <TD CLASS="h">0% (0/97)</TD> | |
96 <TD CLASS="h">0% (0/26)</TD> | |
97 </TR> | |
98 </TABLE> | |
99 <TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%"> | |
100 </TABLE> | |
101 </BODY> | |
102 </HTML>""" | |
103 self.package_2_class_list_html = """ | |
104 <HTML> | |
105 <BODY> | |
106 <TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%"> | |
107 </TABLE> | |
108 <TABLE CELLSPACING="0" WIDTH="100%"> | |
109 </TABLE> | |
110 <TABLE CELLSPACING="0" WIDTH="100%"> | |
111 <TR> | |
112 <TH CLASS="f">name</TH> | |
113 <TH>class, %</TH> | |
114 <TH>method, %</TH> | |
115 <TH>block, %</TH> | |
116 <TH>line, %</TH> | |
117 </TR> | |
118 <TR CLASS="o"> | |
119 <TD><A HREF="1f.html">ContentSetting.java</A></TD> | |
120 <TD CLASS="h">0% (0/1)</TD> | |
121 </TR> | |
122 <TR> | |
123 <TD><A HREF="20.html">DevToolsServer.java</A></TD> | |
124 </TR> | |
125 <TR CLASS="o"> | |
126 <TD><A HREF="21.html">FileProviderHelper.java</A></TD> | |
127 </TR> | |
128 <TR> | |
129 <TD><A HREF="22.html">ContextualMenuBar.java</A></TD> | |
130 </TR> | |
131 <TR CLASS="o"> | |
132 <TD><A HREF="23.html">AccessibilityUtil.java</A></TD> | |
133 </TR> | |
134 <TR> | |
135 <TD><A HREF="24.html">NavigationPopup.java</A></TD> | |
136 </TR> | |
137 </TABLE> | |
138 <TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%"> | |
139 </TABLE> | |
140 </BODY> | |
141 </HTML>""" | |
142 self.partially_covered_tr_html = """ | |
143 <tr class="p"> | |
144 <td class="l" title="78% line coverage (7 out of 9)">108</td> | |
145 <td title="78% line coverage (7 out of 9 instructions)"> | |
146 if (index < 0 || index = mSelectors.size()) index = 0;</td> | |
147 </tr>""" | |
148 self.covered_tr_html = """ | |
149 <tr class="c"> | |
150 <td class="l">110</td> | |
151 <td> if (mSelectors.get(index) != null) {</td> | |
152 </tr>""" | |
153 self.not_executable_tr_html = """ | |
154 <tr> | |
155 <td class="l">109</td> | |
156 <td> </td> | |
157 </tr>""" | |
158 self.tr_with_extra_a_tag = """ | |
159 <tr class="z"> | |
160 <td class="l"> | |
161 <a name="1f">54</a> | |
162 </td> | |
163 <td> }</td> | |
164 </tr>""" | |
165 | |
166 def testInit(self): | |
167 emma_dir = self.emma_dir | |
168 parser = coverage._EmmaHtmlParser(emma_dir) | |
169 self.assertEqual(parser._base_dir, emma_dir) | |
170 self.assertEqual(parser._emma_files_path, 'fake/dir/_files') | |
171 self.assertEqual(parser._index_path, 'fake/dir/index.html') | |
172 | |
173 def testFindElements_basic(self): | |
174 read_values = [self.simple_html] | |
175 found, _ = MockOpenForFunction(self.parser._FindElements, read_values, | |
176 file_path='fake', xpath_selector='//td') | |
177 self.assertIs(type(found), list) | |
178 self.assertIs(type(found[0]), html.HtmlElement) | |
179 self.assertEqual(found[0].text, 'Test HTML') | |
180 | |
181 def testFindElements_noMatch(self): | |
182 read_values = [self.simple_html] | |
183 found, _ = MockOpenForFunction(self.parser._FindElements, read_values, | |
184 file_path='fake', xpath_selector='//tr') | |
185 self.assertEqual(found, []) | |
186 | |
187 def testFindElements_badFilePath(self): | |
188 with self.assertRaises(IOError): | |
189 with mock.patch('os.path.exists', return_value=False): | |
190 self.parser._FindElements('fake', xpath_selector='//tr') | |
191 | |
192 def testGetPackageNameToEmmaFileDict_basic(self): | |
193 expected_dict = { | |
194 'org.chromium.chrome.browser.AccessibilityUtil.java': | |
195 'fake/dir/_files/23.html', | |
196 'org.chromium.chrome.browser.ContextualMenuBar.java': | |
197 'fake/dir/_files/22.html', | |
198 'org.chromium.chrome.browser.tabmodel.IntentHelper.java': | |
199 'fake/dir/_files/1e.html', | |
200 'org.chromium.chrome.browser.ContentSetting.java': | |
201 'fake/dir/_files/1f.html', | |
202 'org.chromium.chrome.browser.DevToolsServer.java': | |
203 'fake/dir/_files/20.html', | |
204 'org.chromium.chrome.browser.NavigationPopup.java': | |
205 'fake/dir/_files/24.html', | |
206 'org.chromium.chrome.browser.FileProviderHelper.java': | |
207 'fake/dir/_files/21.html'} | |
208 | |
209 read_values = [self.index_html, self.package_1_class_list_html, | |
210 self.package_2_class_list_html] | |
211 return_dict, mock_open = MockOpenForFunction( | |
212 self.parser.GetPackageNameToEmmaFileDict, read_values) | |
213 | |
214 self.assertDictEqual(return_dict, expected_dict) | |
215 self.assertEqual(mock_open.call_count, 3) | |
216 calls = [mock.call('fake/dir/index.html'), | |
217 mock.call('fake/dir/_files/1.html'), | |
218 mock.call('fake/dir/_files/0.html')] | |
219 mock_open.assert_has_calls(calls) | |
220 | |
221 def testGetPackageNameToEmmaFileDict_badFilePath(self): | |
222 with self.assertRaises(IOError): | |
223 with mock.patch('os.path.exists', return_value=False): | |
224 self.parser.GetPackageNameToEmmaFileDict() | |
225 | |
226 def testGetLineCoverage_status_basic(self): | |
227 line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html]) | |
228 self.assertEqual(line_coverage[0].covered_status, | |
229 coverage.LineCoverage.COVERED) | |
230 | |
231 def testGetLineCoverage_status_statusMissing(self): | |
232 line_coverage = self.GetLineCoverageWithFakeElements( | |
233 [self.not_executable_tr_html]) | |
234 self.assertEqual(line_coverage[0].covered_status, | |
235 coverage.LineCoverage.NOT_EXECUTABLE) | |
236 | |
237 def testGetLineCoverage_fractionalCoverage_basic(self): | |
238 line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html]) | |
239 self.assertEqual(line_coverage[0].fractional_line_coverage, 1.0) | |
240 | |
241 def testGetLineCoverage_fractionalCoverage_partial(self): | |
242 line_coverage = self.GetLineCoverageWithFakeElements( | |
243 [self.partially_covered_tr_html]) | |
244 self.assertEqual(line_coverage[0].fractional_line_coverage, 0.78) | |
245 | |
246 def testGetLineCoverage_lineno_basic(self): | |
247 line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html]) | |
248 self.assertEqual(line_coverage[0].lineno, 110) | |
249 | |
250 def testGetLineCoverage_lineno_withAlternativeHtml(self): | |
251 line_coverage = self.GetLineCoverageWithFakeElements( | |
252 [self.tr_with_extra_a_tag]) | |
253 self.assertEqual(line_coverage[0].lineno, 54) | |
254 | |
255 def testGetLineCoverage_source(self): | |
256 self.parser._FindElements = mock.Mock( | |
257 return_value=[html.fromstring(self.covered_tr_html)]) | |
258 line_coverage = self.parser.GetLineCoverage('fake_path') | |
259 self.assertEqual(line_coverage[0].source, | |
260 ' if (mSelectors.get(index) != null) {') | |
261 | |
262 def testGetLineCoverage_multipleElements(self): | |
263 line_coverage = self.GetLineCoverageWithFakeElements( | |
264 [self.covered_tr_html, self.partially_covered_tr_html, | |
265 self.tr_with_extra_a_tag]) | |
266 self.assertEqual(len(line_coverage), 3) | |
267 | |
268 def GetLineCoverageWithFakeElements(self, html_elements): | |
269 """Wraps GetLineCoverage to work with extra whitespace characters. | |
270 | |
271 The test HTML strings include extra whitespace characters to make the HTML | |
272 human readable. This isn't the case with EMMA HTML files, so we need to | |
273 remove all the unnecessary whitespace. | |
274 | |
275 Args: | |
276 html_elements: List of strings each representing an HTML element. | |
277 | |
278 Returns: | |
279 A list of LineCoverage objects. | |
280 """ | |
281 elements = [self.MakeElementsWithoutWhitespace(string) | |
282 for string in html_elements] | |
283 with mock.patch('coverage._EmmaHtmlParser._FindElements', | |
284 return_value=elements): | |
285 return self.parser.GetLineCoverage('fake_path') | |
286 | |
287 def MakeElementsWithoutWhitespace(self, html_string): | |
mikecase (-- gone --)
2015/07/13 16:50:07
This seems a little weird. You could have you Html
estevenson1
2015/07/21 00:01:15
Done.
| |
288 """Helper to make HtmlElements with excess whitespace removed. | |
289 | |
290 Args: | |
291 html_string: String representing HTML to be used to create elements. | |
292 | |
293 Returns: | |
294 The root element of |html_string| with all extra whitespace removed. | |
295 """ | |
296 parser = etree.HTMLParser(remove_blank_text=True) | |
297 root = html.fromstring(html_string, parser=parser) | |
298 for element in root.iter('*'): | |
299 if element.text is not None: | |
300 element.text = element.text.strip() | |
301 return root | |
302 | |
303 | |
304 def MockOpenForFunction(func, side_effects, **kwargs): | |
305 """Allows easy mock open and read for callables that open multiple files. | |
306 | |
307 Args: | |
308 func: The callable to invoke once mock files are setup. | |
309 side_effects: A list of return values for each file to return once read. | |
310 Length of list should be equal to the number calls to open in |func|. | |
311 **kwargs: Keyword arguments to be passed to |func|. | |
312 | |
313 Returns: | |
314 A tuple containing the return value of |func| and the MagicMock object used | |
315 to mock all calls to open respectively. | |
316 """ | |
317 mock_open = mock.mock_open() | |
318 mock_open.side_effect = [mock.mock_open(read_data=side_effect).return_value | |
319 for side_effect in side_effects] | |
320 with mock.patch('__builtin__.open', mock_open): | |
321 return func(**kwargs), mock_open | |
322 | |
323 | |
324 if __name__ == '__main__': | |
325 unittest.main() | |
OLD | NEW |