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

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

Issue 1216033009: Updated script to capture useful coverage stats. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Small refactor. 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
« build/android/coverage.py ('K') | « build/android/coverage.py ('k') | no next file » | 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 import json
7 import os
8 import sys
9 import unittest
10 from xml.etree import ElementTree
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 EMPTY_COVERAGE_STATS_DICT = {
20 'files': {},
21 'patch': {
22 'incremental': {
23 'covered': 0, 'total': 0
24 }
25 }
26 }
27
28
29 class _EmmaHtmlParserTest(unittest.TestCase):
30 """Tests for _EmmaHtmlParser.
31
32 Uses modified EMMA report HTML that contains only the subset of tags needed
33 for test verification.
34 """
35
36 def setUp(self):
37 self.emma_dir = 'fake/dir/'
38 self.parser = coverage._EmmaHtmlParser(self.emma_dir)
39 self.simple_html = '<TR><TD CLASS="p">Test HTML</TD></TR>'
40 self.index_html = (
41 '<HTML>'
42 '<BODY>'
43 '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
44 '</TABLE>'
45 '<TABLE CELLSPACING="0" WIDTH="100%">'
46 '</TABLE>'
47 '<TABLE CLASS="it" CELLSPACING="0">'
48 '</TABLE>'
49 '<TABLE CELLSPACING="0" WIDTH="100%">'
50 '<TR>'
51 '<TH CLASS="f">name</TH>'
52 '<TH>class, %</TH>'
53 '<TH>method, %</TH>'
54 '<TH>block, %</TH>'
55 '<TH>line, %</TH>'
56 '</TR>'
57 '<TR CLASS="o">'
58 '<TD><A HREF="_files/0.html"'
59 '>org.chromium.chrome.browser</A></TD>'
60 '<TD CLASS="h">0% (0/3)</TD>'
61 '</TR>'
62 '<TR>'
63 '<TD><A HREF="_files/1.html"'
64 '>org.chromium.chrome.browser.tabmodel</A></TD>'
65 '<TD CLASS="h">0% (0/8)</TD>'
66 '</TR>'
67 '</TABLE>'
68 '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
69 '</TABLE>'
70 '</BODY>'
71 '</HTML>'
72 )
73 self.package_1_class_list_html = (
74 '<HTML>'
75 '<BODY>'
76 '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
77 '</TABLE>'
78 '<TABLE CELLSPACING="0" WIDTH="100%">'
79 '</TABLE>'
80 '<TABLE CELLSPACING="0" WIDTH="100%">'
81 '<TR>'
82 '<TH CLASS="f">name</TH>'
83 '<TH>class, %</TH>'
84 '<TH>method, %</TH>'
85 '<TH>block, %</TH>'
86 '<TH>line, %</TH>'
87 '</TR>'
88 '<TR CLASS="o">'
89 '<TD><A HREF="1e.html">IntentHelper.java</A></TD>'
90 '<TD CLASS="h">0% (0/3)</TD>'
91 '<TD CLASS="h">0% (0/9)</TD>'
92 '<TD CLASS="h">0% (0/97)</TD>'
93 '<TD CLASS="h">0% (0/26)</TD>'
94 '</TR>'
95 '</TABLE>'
96 '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
97 '</TABLE>'
98 '</BODY>'
99 '</HTML>'
100 )
101 self.package_2_class_list_html = (
102 '<HTML>'
103 '<BODY>'
104 '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
105 '</TABLE>'
106 '<TABLE CELLSPACING="0" WIDTH="100%">'
107 '</TABLE>'
108 '<TABLE CELLSPACING="0" WIDTH="100%">'
109 '<TR>'
110 '<TH CLASS="f">name</TH>'
111 '<TH>class, %</TH>'
112 '<TH>method, %</TH>'
113 '<TH>block, %</TH>'
114 '<TH>line, %</TH>'
115 '</TR>'
116 '<TR CLASS="o">'
117 '<TD><A HREF="1f.html">ContentSetting.java</A></TD>'
118 '<TD CLASS="h">0% (0/1)</TD>'
119 '</TR>'
120 '<TR>'
121 '<TD><A HREF="20.html">DevToolsServer.java</A></TD>'
122 '</TR>'
123 '<TR CLASS="o">'
124 '<TD><A HREF="21.html">FileProviderHelper.java</A></TD>'
125 '</TR>'
126 '<TR>'
127 '<TD><A HREF="22.html">ContextualMenuBar.java</A></TD>'
128 '</TR>'
129 '<TR CLASS="o">'
130 '<TD><A HREF="23.html">AccessibilityUtil.java</A></TD>'
131 '</TR>'
132 '<TR>'
133 '<TD><A HREF="24.html">NavigationPopup.java</A></TD>'
134 '</TR>'
135 '</TABLE>'
136 '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
137 '</TABLE>'
138 '</BODY>'
139 '</HTML>'
140 )
141 self.partially_covered_tr_html = (
142 '<TR CLASS="p">'
143 '<TD CLASS="l" TITLE="78% line coverage (7 out of 9)">108</TD>'
144 '<TD TITLE="78% line coverage (7 out of 9 instructions)">'
145 'if (index &lt; 0 || index = mSelectors.size()) index = 0;</TD>'
146 '</TR>'
147 )
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 )
154 self.not_executable_tr_html = (
155 '<TR>'
156 '<TD CLASS="l">109</TD>'
157 '<TD> </TD>'
158 '</TR>'
159 )
160 self.tr_with_extra_a_tag = (
161 '<TR CLASS="z">'
162 '<TD CLASS="l">'
163 '<A name="1f">54</A>'
164 '</TD>'
165 '<TD> }</TD>'
166 '</TR>'
167 )
168
169 def testInit(self):
170 emma_dir = self.emma_dir
171 parser = coverage._EmmaHtmlParser(emma_dir)
172 self.assertEqual(parser._base_dir, emma_dir)
173 self.assertEqual(parser._emma_files_path, 'fake/dir/_files')
174 self.assertEqual(parser._index_path, 'fake/dir/index.html')
175
176 def testFindElements_basic(self):
177 read_values = [self.simple_html]
178 found, _ = MockOpenForFunction(self.parser._FindElements, read_values,
179 file_path='fake', xpath_selector='.//TD')
180 self.assertIs(type(found), list)
181 self.assertIs(type(found[0]), ElementTree.Element)
182 self.assertEqual(found[0].text, 'Test HTML')
183
184 def testFindElements_multipleElements(self):
185 multiple_trs = self.not_executable_tr_html + self.covered_tr_html
186 read_values = ['<div>' + multiple_trs + '</div>']
187 found, _ = MockOpenForFunction(self.parser._FindElements, read_values,
188 file_path='fake', xpath_selector='.//TR')
189 self.assertEquals(2, len(found))
190
191 def testFindElements_noMatch(self):
192 read_values = [self.simple_html]
193 found, _ = MockOpenForFunction(self.parser._FindElements, read_values,
194 file_path='fake', xpath_selector='.//TR')
195 self.assertEqual(found, [])
196
197 def testFindElements_badFilePath(self):
198 with self.assertRaises(IOError):
199 with mock.patch('os.path.exists', return_value=False):
200 self.parser._FindElements('fake', xpath_selector='//tr')
201
202 def testGetPackageNameToEmmaFileDict_basic(self):
203 expected_dict = {
204 'org.chromium.chrome.browser.AccessibilityUtil.java':
205 'fake/dir/_files/23.html',
206 'org.chromium.chrome.browser.ContextualMenuBar.java':
207 'fake/dir/_files/22.html',
208 'org.chromium.chrome.browser.tabmodel.IntentHelper.java':
209 'fake/dir/_files/1e.html',
210 'org.chromium.chrome.browser.ContentSetting.java':
211 'fake/dir/_files/1f.html',
212 'org.chromium.chrome.browser.DevToolsServer.java':
213 'fake/dir/_files/20.html',
214 'org.chromium.chrome.browser.NavigationPopup.java':
215 'fake/dir/_files/24.html',
216 'org.chromium.chrome.browser.FileProviderHelper.java':
217 'fake/dir/_files/21.html'}
218
219 read_values = [self.index_html, self.package_1_class_list_html,
220 self.package_2_class_list_html]
221 return_dict, mock_open = MockOpenForFunction(
222 self.parser.GetPackageNameToEmmaFileDict, read_values)
223
224 self.assertDictEqual(return_dict, expected_dict)
225 self.assertEqual(mock_open.call_count, 3)
226 calls = [mock.call('fake/dir/index.html'),
227 mock.call('fake/dir/_files/1.html'),
228 mock.call('fake/dir/_files/0.html')]
229 mock_open.assert_has_calls(calls)
230
231 def testGetPackageNameToEmmaFileDict_noPackageElements(self):
232 self.parser._FindElements = mock.Mock(return_value=[])
233 return_dict = self.parser.GetPackageNameToEmmaFileDict()
234 self.assertDictEqual({}, return_dict)
235
236 def testGetLineCoverage_status_basic(self):
237 line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html])
238 self.assertEqual(line_coverage[0].covered_status,
239 coverage.COVERED)
240
241 def testGetLineCoverage_status_statusMissing(self):
242 line_coverage = self.GetLineCoverageWithFakeElements(
243 [self.not_executable_tr_html])
244 self.assertEqual(line_coverage[0].covered_status,
245 coverage.NOT_EXECUTABLE)
246
247 def testGetLineCoverage_fractionalCoverage_basic(self):
248 line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html])
249 self.assertEqual(line_coverage[0].fractional_line_coverage, 1.0)
250
251 def testGetLineCoverage_fractionalCoverage_partial(self):
252 line_coverage = self.GetLineCoverageWithFakeElements(
253 [self.partially_covered_tr_html])
254 self.assertEqual(line_coverage[0].fractional_line_coverage, 0.78)
255
256 def testGetLineCoverage_lineno_basic(self):
257 line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html])
258 self.assertEqual(line_coverage[0].lineno, 110)
259
260 def testGetLineCoverage_lineno_withAlternativeHtml(self):
261 line_coverage = self.GetLineCoverageWithFakeElements(
262 [self.tr_with_extra_a_tag])
263 self.assertEqual(line_coverage[0].lineno, 54)
264
265 def testGetLineCoverage_source(self):
266 self.parser._FindElements = mock.Mock(
267 return_value=[ElementTree.fromstring(self.covered_tr_html)])
268 line_coverage = self.parser.GetLineCoverage('fake_path')
269 self.assertEqual(line_coverage[0].source,
270 ' if (mSelectors.get(index) != null) {')
271
272 def testGetLineCoverage_multipleElements(self):
273 line_coverage = self.GetLineCoverageWithFakeElements(
274 [self.covered_tr_html, self.partially_covered_tr_html,
275 self.tr_with_extra_a_tag])
276 self.assertEqual(len(line_coverage), 3)
277
278 def GetLineCoverageWithFakeElements(self, html_elements):
279 """Wraps GetLineCoverage to work with extra whitespace characters.
280
281 The test HTML strings include extra whitespace characters to make the HTML
282 human readable. This isn't the case with EMMA HTML files, so we need to
283 remove all the unnecessary whitespace.
284
285 Args:
286 html_elements: List of strings each representing an HTML element.
287
288 Returns:
289 A list of LineCoverage objects.
290 """
291 elements = [ElementTree.fromstring(string) for string in html_elements]
292 with mock.patch('coverage._EmmaHtmlParser._FindElements',
293 return_value=elements):
294 return self.parser.GetLineCoverage('fake_path')
295
296
297 class _EmmaCoverageStatsTest(unittest.TestCase):
298 """Tests for _EmmaCoverageStats."""
299
300 def setUp(self):
301 self.good_source_to_emma = {
302 '/path/to/1/File1.java': '/emma/1.html',
303 '/path/2/File2.java': '/emma/2.html',
304 '/path/2/File3.java': '/emma/3.html'
305 }
306 self.line_coverage = [
307 coverage.LineCoverage(1, '', coverage.COVERED, 1.0),
308 coverage.LineCoverage(2, '', coverage.COVERED, 1.0),
309 coverage.LineCoverage(3, '', coverage.NOT_EXECUTABLE, 1.0),
310 coverage.LineCoverage(4, '', coverage.NOT_COVERED, 1.0),
311 coverage.LineCoverage(5, '',
312 coverage.PARTIALLY_COVERED, 0.85),
313 coverage.LineCoverage(6, '',
314 coverage.PARTIALLY_COVERED, 0.20)
315 ]
316 self.lines_for_coverage = [1, 3, 5, 6]
317 with mock.patch('coverage._EmmaHtmlParser._FindElements', return_value=[]):
318 self.simple_coverage = coverage._EmmaCoverageStats('fake_dir', {})
319
320 def testInit(self):
321 coverage_stats = self.simple_coverage
322 self.assertIsInstance(coverage_stats._emma_parser,
323 coverage._EmmaHtmlParser)
324 self.assertIsInstance(coverage_stats._source_to_emma, dict)
325
326 def testNeedsCoverage_withExistingJavaFile(self):
327 test_file = '/path/to/file/File.java'
328 with mock.patch('os.path.exists', return_value=True):
329 self.assertTrue(coverage._EmmaCoverageStats.NeedsCoverage(test_file))
330
331 def testNeedsCoverage_withNonJavaFile(self):
332 test_file = '/path/to/file/File.c'
333 with mock.patch('os.path.exists', return_value=True):
334 self.assertFalse(coverage._EmmaCoverageStats.NeedsCoverage(test_file))
335
336 def testNeedsCoverage_fileDoesNotExist(self):
337 test_file = '/path/to/file/File.java'
338 with mock.patch('os.path.exists', return_value=False):
339 self.assertFalse(coverage._EmmaCoverageStats.NeedsCoverage(test_file))
340
341 def testGetPackageNameFromFile_basic(self):
342 test_file_text = """// Test Copyright
343 package org.chromium.chrome.browser;
344 import android.graphics.RectF;"""
345 result_package, _ = MockOpenForFunction(
346 coverage._EmmaCoverageStats.GetPackageNameFromFile, [test_file_text],
347 file_path='/path/to/file/File.java')
348 self.assertEqual(result_package, 'org.chromium.chrome.browser.File.java')
349
350 def testGetPackageNameFromFile_noPackageStatement(self):
351 result_package, _ = MockOpenForFunction(
352 coverage._EmmaCoverageStats.GetPackageNameFromFile,
353 ['not a package statement'], file_path='/path/to/file/File.java')
354 self.assertIsNone(result_package)
355
356 def testGetStatsForLines_basic(self):
357 covered, total = self.simple_coverage.GetStatsForLines(
358 self.line_coverage, self.lines_for_coverage)
359 self.assertEqual(covered, 2.05)
360 self.assertEqual(total, 3)
361
362 def testGetStatsForLines_noLineNumbersProvided(self):
363 covered, total = self.simple_coverage.GetStatsForLines(self.line_coverage)
364 self.assertEqual(covered, 3.05)
365 self.assertEqual(total, 5)
366
367 def testGetSourceFileToEmmaFileDict(self):
368 package_names = {
369 '/path/to/1/File1.java': 'org.fake.one.File1.java',
370 '/path/2/File2.java': 'org.fake.File2.java',
371 '/path/2/File3.java': 'org.fake.File3.java'
372 }
373 package_to_emma = {
374 'org.fake.one.File1.java': '/emma/1.html',
375 'org.fake.File2.java': '/emma/2.html',
376 'org.fake.File3.java': '/emma/3.html'
377 }
378 with mock.patch('os.path.exists', return_value=True):
379 coverage_stats = self.simple_coverage
380 coverage_stats._emma_parser.GetPackageNameToEmmaFileDict = mock.MagicMock(
381 return_value=package_to_emma)
382 coverage_stats.GetPackageNameFromFile = lambda x: package_names[x]
383 result_dict = coverage_stats._GetSourceFileToEmmaFileDict(
384 package_names.keys())
385 self.assertDictEqual(result_dict, self.good_source_to_emma)
386
387 def testGetCoverageStatusForFile_basic(self):
388 java_file_path = '/path/to/1/File1.java'
389 line_coverage = [
390 coverage.LineCoverage(1, '', coverage.COVERED, 1.0)]
391 coverage_stats = self.simple_coverage
392 coverage_stats._source_to_emma = self.good_source_to_emma
393 coverage_stats._emma_parser.GetLineCoverage = mock.MagicMock(
394 return_value=line_coverage)
395 coverage_info = coverage_stats._GetCoverageStatusForFile(java_file_path)
396 self.assertListEqual(coverage_info, line_coverage)
397
398 def testGetCoverageStatusForFile_noInfo(self):
399 coverage_info = self.simple_coverage._GetCoverageStatusForFile('fake_path')
400 self.assertIsNone(coverage_info)
401
402 def testGetCoverageReportForLines(self):
403 line_coverage = self.line_coverage
404 lines = self.lines_for_coverage
405 expected_dict = {
406 'absolute': {
407 'covered': 3.05,
408 'total': 5
409 },
410 'incremental': {
411 'covered': 2.05,
412 'total': 3
413 },
414 'source': [
415 {
416 'line': line_coverage[0].source,
417 'coverage': line_coverage[0].covered_status,
418 'changed': True
419 },
420 {
421 'line': line_coverage[1].source,
422 'coverage': line_coverage[1].covered_status,
423 'changed': False
424 },
425 {
426 'line': line_coverage[2].source,
427 'coverage': line_coverage[2].covered_status,
428 'changed': True
429 },
430 {
431 'line': line_coverage[3].source,
432 'coverage': line_coverage[3].covered_status,
433 'changed': False
434 },
435 {
436 'line': line_coverage[4].source,
437 'coverage': line_coverage[4].covered_status,
438 'changed': True
439 },
440 {
441 'line': line_coverage[5].source,
442 'coverage': line_coverage[5].covered_status,
443 'changed': True
444 }
445 ]
446 }
447 result_dict = self.simple_coverage.GetCoverageReportForLines(line_coverage,
448 lines)
449 self.assertDictEqual(result_dict, expected_dict)
450
451 def testGetCoverageReportForLines_emptyCoverage(self):
452 expected_dict = {
453 'absolute': {'covered': 0, 'total': 0},
454 'incremental': {'covered': 0, 'total': 0},
455 'source': []
456 }
457 result_dict = self.simple_coverage.GetCoverageReportForLines({}, [])
458 self.assertDictEqual(result_dict, expected_dict)
459
460 def testGetCoverageDictForFiles_basic(self):
461 files_for_coverage = {
462 '/path/to/1/File1.java': [1, 3, 4],
463 '/path/2/File2.java': [1, 2]
464 }
465 coverage_info = {
466 '/path/to/1/File1.java': [
467 coverage.LineCoverage(1, '', coverage.COVERED, 1.0),
468 coverage.LineCoverage(2, '',
469 coverage.PARTIALLY_COVERED, 0.5),
470 coverage.LineCoverage(3, '',
471 coverage.NOT_EXECUTABLE, 1.0),
472 coverage.LineCoverage(4, '', coverage.COVERED, 1.0)
473 ],
474 '/path/2/File2.java': [
475 coverage.LineCoverage(1, '', coverage.NOT_COVERED, 1.0),
476 coverage.LineCoverage(2, '', coverage.COVERED, 1.0)
477 ]
478 }
479 expected_dict = {
480 'files': {
481 '/path/2/File2.java': {
482 'absolute': {'covered': 1, 'total': 2},
483 'incremental': {'covered': 1, 'total': 2},
484 'source': [{'changed': True, 'coverage': 0, 'line': ''},
485 {'changed': True, 'coverage': 1, 'line': ''}]
486 },
487 '/path/to/1/File1.java': {
488 'absolute': {'covered': 2.5, 'total': 3},
489 'incremental': {'covered': 2, 'total': 2},
490 'source': [{'changed': True, 'coverage': 1, 'line': ''},
491 {'changed': False, 'coverage': 2, 'line': ''},
492 {'changed': True, 'coverage': -1, 'line': ''},
493 {'changed': True, 'coverage': 1, 'line': ''}]
494 }
495 },
496 'patch': {'incremental': {'covered': 3, 'total': 4}}
497 }
498 # Return the relevant coverage info for each file. We aren't testing
499 # _GetCoverageStatusForFile here.
500 self.simple_coverage._GetCoverageStatusForFile = lambda x: coverage_info[x]
501 result_dict = self.simple_coverage.GetCoverageDictForFiles(
502 files_for_coverage)
503 self.assertDictEqual(result_dict, expected_dict)
504
505 def testGetCoverageDictForFiles_noCoverage(self):
506 result_dict = self.simple_coverage.GetCoverageDictForFiles({})
507 self.assertDictEqual(result_dict, EMPTY_COVERAGE_STATS_DICT)
508
509
510 class CoverageGenerateCoverageReportTest(unittest.TestCase):
511 """Tests for GenerateCoverageReport."""
512
513 def testGenerateCoverageReport_noCoverageRequired(self):
514 with self.assertRaises(SystemExit) as cm:
515 with mock.patch('os.path.exists', return_value=True):
516 # Returning an empty dictionary on the first call to read() will cause
517 # the method to fail gracefully. This will happen if there are no Java
518 # files to perform coverage analysis on.
519 MockOpenForFunction(coverage.GenerateCoverageReport, ['{}'],
520 line_coverage_file='', out_file_path='',
521 coverage_dir='')
522 self.assertEqual(cm.exception.code, 0)
523
524 @mock.patch('json.dump')
525 @mock.patch('coverage._EmmaCoverageStats')
526 def testGenerateCoverageReport_basic(self, mock_stats, mock_dump):
527 file_changes = '{"file1.java":[1,2,3]}'
528 # Make GetCoverageDict return a valid dictionary as expected in
529 # GenerateCoverageReport.
530 mock_get_coverage_dict = mock_stats.return_value.GetCoverageDictForFiles
531 mock_get_coverage_dict.return_value = EMPTY_COVERAGE_STATS_DICT
532 with mock.patch('os.path.exists', return_value=True):
533 # Call GenerateCoverageReport with a valid set of file changes so that
534 # the function can run to completion.
535 MockOpenForFunction(coverage.GenerateCoverageReport, [file_changes, ''],
536 line_coverage_file='', out_file_path='',
537 coverage_dir='')
538 # Assert that the method ran to completion, and that json.dump was
539 # successfully called.
540 self.assertEqual(1, mock_dump.call_count)
541
542
543 def MockOpenForFunction(func, side_effects, **kwargs):
544 """Allows easy mock open and read for callables that open multiple files.
545
546 Will mock the python open function in a way such that each time read() is
547 called on an open file, the next element in |side_effects| is returned. This
548 makes it easier to test functions that call open() multiple times.
549
550 Args:
551 func: The callable to invoke once mock files are setup.
552 side_effects: A list of return values for each file to return once read.
553 Length of list should be equal to the number calls to open in |func|.
554 **kwargs: Keyword arguments to be passed to |func|.
555
556 Returns:
557 A tuple containing the return value of |func| and the MagicMock object used
558 to mock all calls to open respectively.
559 """
560 mock_open = mock.mock_open()
561 mock_open.side_effect = [mock.mock_open(read_data=side_effect).return_value
562 for side_effect in side_effects]
563 with mock.patch('__builtin__.open', mock_open):
564 return func(**kwargs), mock_open
565
566
567 if __name__ == '__main__':
568 # Suppress logging messages.
569 unittest.main(buffer=True)
OLDNEW
« build/android/coverage.py ('K') | « build/android/coverage.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698