Chromium Code Reviews

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

Issue 1216033009: Updated script to capture useful coverage stats. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed jbudorick's comments. Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
« no previous file with comments | « build/android/emma_coverage_stats.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
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright 2015 The Chromium Authors. All rights reserved. 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 import json
6 import os 7 import os
7 import sys 8 import sys
8 import unittest 9 import unittest
9 from xml.etree import ElementTree 10 from xml.etree import ElementTree
10 11
11 import emma_coverage_stats 12 import emma_coverage_stats
12 from pylib import constants 13 from pylib import constants
13 14
14 sys.path.append(os.path.join( 15 sys.path.append(os.path.join(
15 constants.DIR_SOURCE_ROOT, 'third_party', 'pymock')) 16 constants.DIR_SOURCE_ROOT, 'third_party', 'pymock'))
16 import mock # pylint: disable=F0401 17 import mock # pylint: disable=F0401
17 18
19 EMPTY_COVERAGE_STATS_DICT = {
20 'files': {},
21 'patch': {
22 'incremental': {
23 'covered': 0, 'total': 0
24 }
25 }
26 }
27
18 28
19 class _EmmaHtmlParserTest(unittest.TestCase): 29 class _EmmaHtmlParserTest(unittest.TestCase):
20 """Tests for _EmmaHtmlParser. 30 """Tests for _EmmaHtmlParser.
21 31
22 Uses modified EMMA report HTML that contains only the subset of tags needed 32 Uses modified EMMA report HTML that contains only the subset of tags needed
23 for test verification. 33 for test verification.
24 """ 34 """
25 35
26 def setUp(self): 36 def setUp(self):
27 self.emma_dir = 'fake/dir/' 37 self.emma_dir = 'fake/dir/'
(...skipping 188 matching lines...)
216 calls = [mock.call('fake/dir/index.html'), 226 calls = [mock.call('fake/dir/index.html'),
217 mock.call('fake/dir/_files/1.html'), 227 mock.call('fake/dir/_files/1.html'),
218 mock.call('fake/dir/_files/0.html')] 228 mock.call('fake/dir/_files/0.html')]
219 mock_open.assert_has_calls(calls) 229 mock_open.assert_has_calls(calls)
220 230
221 def testGetPackageNameToEmmaFileDict_noPackageElements(self): 231 def testGetPackageNameToEmmaFileDict_noPackageElements(self):
222 self.parser._FindElements = mock.Mock(return_value=[]) 232 self.parser._FindElements = mock.Mock(return_value=[])
223 return_dict = self.parser.GetPackageNameToEmmaFileDict() 233 return_dict = self.parser.GetPackageNameToEmmaFileDict()
224 self.assertDictEqual({}, return_dict) 234 self.assertDictEqual({}, return_dict)
225 235
236 def testGetPackageNameToEmmaFileDict_badFilePath(self):
237 self.parser._FindElements = mock.Mock(return_value=[])
238 return_dict = self.parser.GetPackageNameToEmmaFileDict()
239 self.assertEqual(return_dict, {})
240
226 def testGetLineCoverage_status_basic(self): 241 def testGetLineCoverage_status_basic(self):
227 line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html]) 242 line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html])
228 self.assertEqual(line_coverage[0].covered_status, 243 self.assertEqual(line_coverage[0].covered_status,
229 emma_coverage_stats.COVERED) 244 emma_coverage_stats.COVERED)
230 245
231 def testGetLineCoverage_status_statusMissing(self): 246 def testGetLineCoverage_status_statusMissing(self):
232 line_coverage = self.GetLineCoverageWithFakeElements( 247 line_coverage = self.GetLineCoverageWithFakeElements(
233 [self.not_executable_tr_html]) 248 [self.not_executable_tr_html])
234 self.assertEqual(line_coverage[0].covered_status, 249 self.assertEqual(line_coverage[0].covered_status,
235 emma_coverage_stats.NOT_EXECUTABLE) 250 emma_coverage_stats.NOT_EXECUTABLE)
(...skipping 41 matching lines...)
277 292
278 Returns: 293 Returns:
279 A list of LineCoverage objects. 294 A list of LineCoverage objects.
280 """ 295 """
281 elements = [ElementTree.fromstring(string) for string in html_elements] 296 elements = [ElementTree.fromstring(string) for string in html_elements]
282 with mock.patch('emma_coverage_stats._EmmaHtmlParser._FindElements', 297 with mock.patch('emma_coverage_stats._EmmaHtmlParser._FindElements',
283 return_value=elements): 298 return_value=elements):
284 return self.parser.GetLineCoverage('fake_path') 299 return self.parser.GetLineCoverage('fake_path')
285 300
286 301
302 class _EmmaCoverageStatsTest(unittest.TestCase):
303 """Tests for _EmmaCoverageStats."""
304
305 def setUp(self):
306 self.good_source_to_emma = {
307 '/path/to/1/File1.java': '/emma/1.html',
308 '/path/2/File2.java': '/emma/2.html',
309 '/path/2/File3.java': '/emma/3.html'
310 }
311 self.line_coverage = [
312 emma_coverage_stats.LineCoverage(
313 1, '', emma_coverage_stats.COVERED, 1.0),
314 emma_coverage_stats.LineCoverage(
315 2, '', emma_coverage_stats.COVERED, 1.0),
316 emma_coverage_stats.LineCoverage(
317 3, '', emma_coverage_stats.NOT_EXECUTABLE, 1.0),
318 emma_coverage_stats.LineCoverage(
319 4, '', emma_coverage_stats.NOT_COVERED, 1.0),
320 emma_coverage_stats.LineCoverage(
321 5, '', emma_coverage_stats.PARTIALLY_COVERED, 0.85),
322 emma_coverage_stats.LineCoverage(
323 6, '', emma_coverage_stats.PARTIALLY_COVERED, 0.20)
324 ]
325 self.lines_for_coverage = [1, 3, 5, 6]
326 with mock.patch('emma_coverage_stats._EmmaHtmlParser._FindElements',
327 return_value=[]):
328 self.simple_coverage = emma_coverage_stats._EmmaCoverageStats(
329 'fake_dir', {})
330
331 def testInit(self):
332 coverage_stats = self.simple_coverage
333 self.assertIsInstance(coverage_stats._emma_parser,
334 emma_coverage_stats._EmmaHtmlParser)
335 self.assertIsInstance(coverage_stats._source_to_emma, dict)
336
337 def testNeedsCoverage_withExistingJavaFile(self):
338 test_file = '/path/to/file/File.java'
339 with mock.patch('os.path.exists', return_value=True):
340 self.assertTrue(
341 emma_coverage_stats._EmmaCoverageStats.NeedsCoverage(test_file))
342
343 def testNeedsCoverage_withNonJavaFile(self):
344 test_file = '/path/to/file/File.c'
345 with mock.patch('os.path.exists', return_value=True):
346 self.assertFalse(
347 emma_coverage_stats._EmmaCoverageStats.NeedsCoverage(test_file))
348
349 def testNeedsCoverage_fileDoesNotExist(self):
350 test_file = '/path/to/file/File.java'
351 with mock.patch('os.path.exists', return_value=False):
352 self.assertFalse(
353 emma_coverage_stats._EmmaCoverageStats.NeedsCoverage(test_file))
354
355 def testGetPackageNameFromFile_basic(self):
356 test_file_text = """// Test Copyright
357 package org.chromium.chrome.browser;
358 import android.graphics.RectF;"""
359 result_package, _ = MockOpenForFunction(
360 emma_coverage_stats._EmmaCoverageStats.GetPackageNameFromFile,
361 [test_file_text], file_path='/path/to/file/File.java')
362 self.assertEqual(result_package, 'org.chromium.chrome.browser.File.java')
363
364 def testGetPackageNameFromFile_noPackageStatement(self):
365 result_package, _ = MockOpenForFunction(
366 emma_coverage_stats._EmmaCoverageStats.GetPackageNameFromFile,
367 ['not a package statement'], file_path='/path/to/file/File.java')
368 self.assertIsNone(result_package)
369
370 def testGetSummaryStatsForLines_basic(self):
371 covered, total = self.simple_coverage.GetSummaryStatsForLines(
372 self.line_coverage)
373 self.assertEqual(covered, 3.05)
374 self.assertEqual(total, 5)
375
376 def testGetSourceFileToEmmaFileDict(self):
377 package_names = {
378 '/path/to/1/File1.java': 'org.fake.one.File1.java',
379 '/path/2/File2.java': 'org.fake.File2.java',
380 '/path/2/File3.java': 'org.fake.File3.java'
381 }
382 package_to_emma = {
383 'org.fake.one.File1.java': '/emma/1.html',
384 'org.fake.File2.java': '/emma/2.html',
385 'org.fake.File3.java': '/emma/3.html'
386 }
387 with mock.patch('os.path.exists', return_value=True):
388 coverage_stats = self.simple_coverage
389 coverage_stats._emma_parser.GetPackageNameToEmmaFileDict = mock.MagicMock(
390 return_value=package_to_emma)
391 coverage_stats.GetPackageNameFromFile = lambda x: package_names[x]
392 result_dict = coverage_stats._GetSourceFileToEmmaFileDict(
393 package_names.keys())
394 self.assertDictEqual(result_dict, self.good_source_to_emma)
395
396 def testGetLineCoverageForFile_basic(self):
397 java_file_path = '/path/to/1/File1.java'
398 line_coverage = emma_coverage_stats.LineCoverage(
399 1, '', emma_coverage_stats.COVERED, 1.0)
400 expected_line_coverage = list(line_coverage)
401 coverage_stats = self.simple_coverage
402 coverage_stats._source_to_emma = self.good_source_to_emma
403 coverage_stats._emma_parser.GetLineCoverage = mock.MagicMock(
404 return_value=expected_line_coverage)
405 coverage_info = coverage_stats._GetLineCoverageForFile(java_file_path)
406 self.assertListEqual(coverage_info, expected_line_coverage)
407
408 def testGetLineCoverageForFile_noInfo(self):
409 with mock.patch('os.path.exists', return_value=False):
410 coverage_info = self.simple_coverage._GetLineCoverageForFile('fake_path')
411 self.assertIsNone(coverage_info)
412
413 def testGetCoverageDictForFile(self):
414 line_coverage = self.line_coverage
415 self.simple_coverage._GetLineCoverageForFile = mock.Mock(
416 return_value=line_coverage)
417 lines = self.lines_for_coverage
418 expected_dict = {
419 'absolute': {
420 'covered': 3.05,
421 'total': 5
422 },
423 'incremental': {
424 'covered': 2.05,
425 'total': 3
426 },
427 'source': [
428 {
429 'line': line_coverage[0].source,
430 'coverage': line_coverage[0].covered_status,
431 'changed': True
432 },
433 {
434 'line': line_coverage[1].source,
435 'coverage': line_coverage[1].covered_status,
436 'changed': False
437 },
438 {
439 'line': line_coverage[2].source,
440 'coverage': line_coverage[2].covered_status,
441 'changed': True
442 },
443 {
444 'line': line_coverage[3].source,
445 'coverage': line_coverage[3].covered_status,
446 'changed': False
447 },
448 {
449 'line': line_coverage[4].source,
450 'coverage': line_coverage[4].covered_status,
451 'changed': True
452 },
453 {
454 'line': line_coverage[5].source,
455 'coverage': line_coverage[5].covered_status,
456 'changed': True
457 }
458 ]
459 }
460 result_dict = self.simple_coverage.GetCoverageDictForFile(
461 line_coverage, lines)
462 self.assertDictEqual(result_dict, expected_dict)
463
464 def testGetCoverageDictForFile_emptyCoverage(self):
465 expected_dict = {
466 'absolute': {'covered': 0, 'total': 0},
467 'incremental': {'covered': 0, 'total': 0},
468 'source': []
469 }
470 self.simple_coverage._GetLineCoverageForFile = mock.Mock(return_value=[])
471 result_dict = self.simple_coverage.GetCoverageDictForFile('fake_dir', {})
472 self.assertDictEqual(result_dict, expected_dict)
473
474 def testGetCoverageDictFor_basic(self):
475 files_for_coverage = {
476 '/path/to/1/File1.java': [1, 3, 4],
477 '/path/2/File2.java': [1, 2]
478 }
479 coverage_info = {
480 '/path/to/1/File1.java': [
481 emma_coverage_stats.LineCoverage(
482 1, '', emma_coverage_stats.COVERED, 1.0),
483 emma_coverage_stats.LineCoverage(
484 2, '', emma_coverage_stats.PARTIALLY_COVERED, 0.5),
485 emma_coverage_stats.LineCoverage(
486 3, '', emma_coverage_stats.NOT_EXECUTABLE, 1.0),
487 emma_coverage_stats.LineCoverage(
488 4, '', emma_coverage_stats.COVERED, 1.0)
489 ],
490 '/path/2/File2.java': [
491 emma_coverage_stats.LineCoverage(
492 1, '', emma_coverage_stats.NOT_COVERED, 1.0),
493 emma_coverage_stats.LineCoverage(
494 2, '', emma_coverage_stats.COVERED, 1.0)
495 ]
496 }
497 expected_dict = {
498 'files': {
499 '/path/2/File2.java': {
500 'absolute': {'covered': 1, 'total': 2},
501 'incremental': {'covered': 1, 'total': 2},
502 'source': [{'changed': True, 'coverage': 0, 'line': ''},
503 {'changed': True, 'coverage': 1, 'line': ''}]
504 },
505 '/path/to/1/File1.java': {
506 'absolute': {'covered': 2.5, 'total': 3},
507 'incremental': {'covered': 2, 'total': 2},
508 'source': [{'changed': True, 'coverage': 1, 'line': ''},
509 {'changed': False, 'coverage': 2, 'line': ''},
510 {'changed': True, 'coverage': -1, 'line': ''},
511 {'changed': True, 'coverage': 1, 'line': ''}]
512 }
513 },
514 'patch': {'incremental': {'covered': 3, 'total': 4}}
515 }
516 # Return the relevant coverage info for each file. We aren't testing
517 # _GetCoverageStatusForFile here.
518 self.simple_coverage._GetLineCoverageForFile = lambda x: coverage_info[x]
519 result_dict = self.simple_coverage.GetCoverageDict(
520 files_for_coverage)
521 self.assertDictEqual(result_dict, expected_dict)
522
523 def testGetCoverageDict_noCoverage(self):
524 result_dict = self.simple_coverage.GetCoverageDict({})
525 self.assertDictEqual(result_dict, EMPTY_COVERAGE_STATS_DICT)
526
527
528 class EmmaCoverageStatsGenerateCoverageReport(unittest.TestCase):
529 """Tests for GenerateCoverageReport."""
530
531 def testGenerateCoverageReport_missingJsonFile(self):
532 with self.assertRaises(IOError):
533 with mock.patch('os.path.exists', return_value=False):
534 emma_coverage_stats.GenerateCoverageReport('', '', '')
535
536 def testGenerateCoverageReport_invalidJsonFile(self):
537 with self.assertRaises(ValueError):
538 with mock.patch('os.path.exists', return_value=True):
539 MockOpenForFunction(emma_coverage_stats.GenerateCoverageReport, [''],
540 line_coverage_file='', out_file_path='',
541 coverage_dir='')
542
543
287 def MockOpenForFunction(func, side_effects, **kwargs): 544 def MockOpenForFunction(func, side_effects, **kwargs):
288 """Allows easy mock open and read for callables that open multiple files. 545 """Allows easy mock open and read for callables that open multiple files.
289 546
547 Will mock the python open function in a way such that each time read() is
548 called on an open file, the next element in |side_effects| is returned. This
549 makes it easier to test functions that call open() multiple times.
550
290 Args: 551 Args:
291 func: The callable to invoke once mock files are setup. 552 func: The callable to invoke once mock files are setup.
292 side_effects: A list of return values for each file to return once read. 553 side_effects: A list of return values for each file to return once read.
293 Length of list should be equal to the number calls to open in |func|. 554 Length of list should be equal to the number calls to open in |func|.
294 **kwargs: Keyword arguments to be passed to |func|. 555 **kwargs: Keyword arguments to be passed to |func|.
295 556
296 Returns: 557 Returns:
297 A tuple containing the return value of |func| and the MagicMock object used 558 A tuple containing the return value of |func| and the MagicMock object used
298 to mock all calls to open respectively. 559 to mock all calls to open respectively.
299 """ 560 """
300 mock_open = mock.mock_open() 561 mock_open = mock.mock_open()
301 mock_open.side_effect = [mock.mock_open(read_data=side_effect).return_value 562 mock_open.side_effect = [mock.mock_open(read_data=side_effect).return_value
302 for side_effect in side_effects] 563 for side_effect in side_effects]
303 with mock.patch('__builtin__.open', mock_open): 564 with mock.patch('__builtin__.open', mock_open):
304 return func(**kwargs), mock_open 565 return func(**kwargs), mock_open
305 566
306 567
307 if __name__ == '__main__': 568 if __name__ == '__main__':
308 unittest.main() 569 # Suppress logging messages.
570 unittest.main(buffer=True)
OLDNEW
« no previous file with comments | « build/android/emma_coverage_stats.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine