#!/usr/bin/env python # Copyright 2015 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # pylint: disable=protected-access import unittest from xml.etree import ElementTree import emma_coverage_stats from pylib.constants import host_paths with host_paths.SysPath(host_paths.PYMOCK_PATH): import mock # pylint: disable=import-error EMPTY_COVERAGE_STATS_DICT = { 'files': {}, 'patch': { 'incremental': { 'covered': 0, 'total': 0 } } } class _EmmaHtmlParserTest(unittest.TestCase): """Tests for _EmmaHtmlParser. Uses modified EMMA report HTML that contains only the subset of tags needed for test verification. """ def setUp(self): self.emma_dir = 'fake/dir/' self.parser = emma_coverage_stats._EmmaHtmlParser(self.emma_dir) self.simple_html = 'Test HTML' self.index_html = ( '' '' '' '
' '' '
' '' '
' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '
nameclass, %method, %block, %line, %
org.chromium.chrome.browser0% (0/3)
org.chromium.chrome.browser.tabmodel0% (0/8)
' '' '
' '' '' ) self.package_1_class_list_html = ( '' '' '' '
' '' '
' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '
nameclass, %method, %block, %line, %
IntentHelper.java0% (0/3)0% (0/9)0% (0/97)0% (0/26)
' '' '
' '' '' ) self.package_2_class_list_html = ( '' '' '' '
' '' '
' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '
nameclass, %method, %block, %line, %
ContentSetting.java0% (0/1)
DevToolsServer.java
FileProviderHelper.java
ContextualMenuBar.java
AccessibilityUtil.java
NavigationPopup.java
' '' '
' '' '' ) self.partially_covered_tr_html = ( '' '108' '' 'if (index < 0 || index = mSelectors.size()) index = 0;' '' ) self.covered_tr_html = ( '' '110' ' if (mSelectors.get(index) != null) {' '' ) self.not_executable_tr_html = ( '' '109' ' ' '' ) self.tr_with_extra_a_tag = ( '' '' '54' '' ' }' '' ) def testInit(self): emma_dir = self.emma_dir parser = emma_coverage_stats._EmmaHtmlParser(emma_dir) self.assertEqual(parser._base_dir, emma_dir) self.assertEqual(parser._emma_files_path, 'fake/dir/_files') self.assertEqual(parser._index_path, 'fake/dir/index.html') def testFindElements_basic(self): read_values = [self.simple_html] found, _ = MockOpenForFunction(self.parser._FindElements, read_values, file_path='fake', xpath_selector='.//TD') self.assertIs(type(found), list) self.assertIs(type(found[0]), ElementTree.Element) self.assertEqual(found[0].text, 'Test HTML') def testFindElements_multipleElements(self): multiple_trs = self.not_executable_tr_html + self.covered_tr_html read_values = ['
' + multiple_trs + '
'] found, _ = MockOpenForFunction(self.parser._FindElements, read_values, file_path='fake', xpath_selector='.//TR') self.assertEquals(2, len(found)) def testFindElements_noMatch(self): read_values = [self.simple_html] found, _ = MockOpenForFunction(self.parser._FindElements, read_values, file_path='fake', xpath_selector='.//TR') self.assertEqual(found, []) def testFindElements_badFilePath(self): with self.assertRaises(IOError): with mock.patch('os.path.exists', return_value=False): self.parser._FindElements('fake', xpath_selector='//tr') def testGetPackageNameToEmmaFileDict_basic(self): expected_dict = { 'org.chromium.chrome.browser.AccessibilityUtil.java': 'fake/dir/_files/23.html', 'org.chromium.chrome.browser.ContextualMenuBar.java': 'fake/dir/_files/22.html', 'org.chromium.chrome.browser.tabmodel.IntentHelper.java': 'fake/dir/_files/1e.html', 'org.chromium.chrome.browser.ContentSetting.java': 'fake/dir/_files/1f.html', 'org.chromium.chrome.browser.DevToolsServer.java': 'fake/dir/_files/20.html', 'org.chromium.chrome.browser.NavigationPopup.java': 'fake/dir/_files/24.html', 'org.chromium.chrome.browser.FileProviderHelper.java': 'fake/dir/_files/21.html'} read_values = [self.index_html, self.package_1_class_list_html, self.package_2_class_list_html] return_dict, mock_open = MockOpenForFunction( self.parser.GetPackageNameToEmmaFileDict, read_values) self.assertDictEqual(return_dict, expected_dict) self.assertEqual(mock_open.call_count, 3) calls = [mock.call('fake/dir/index.html'), mock.call('fake/dir/_files/1.html'), mock.call('fake/dir/_files/0.html')] mock_open.assert_has_calls(calls) def testGetPackageNameToEmmaFileDict_noPackageElements(self): self.parser._FindElements = mock.Mock(return_value=[]) return_dict = self.parser.GetPackageNameToEmmaFileDict() self.assertDictEqual({}, return_dict) def testGetLineCoverage_status_basic(self): line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html]) self.assertEqual(line_coverage[0].covered_status, emma_coverage_stats.COVERED) def testGetLineCoverage_status_statusMissing(self): line_coverage = self.GetLineCoverageWithFakeElements( [self.not_executable_tr_html]) self.assertEqual(line_coverage[0].covered_status, emma_coverage_stats.NOT_EXECUTABLE) def testGetLineCoverage_fractionalCoverage_basic(self): line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html]) self.assertEqual(line_coverage[0].fractional_line_coverage, 1.0) def testGetLineCoverage_fractionalCoverage_partial(self): line_coverage = self.GetLineCoverageWithFakeElements( [self.partially_covered_tr_html]) self.assertEqual(line_coverage[0].fractional_line_coverage, 0.78) def testGetLineCoverage_lineno_basic(self): line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html]) self.assertEqual(line_coverage[0].lineno, 110) def testGetLineCoverage_lineno_withAlternativeHtml(self): line_coverage = self.GetLineCoverageWithFakeElements( [self.tr_with_extra_a_tag]) self.assertEqual(line_coverage[0].lineno, 54) def testGetLineCoverage_source(self): self.parser._FindElements = mock.Mock( return_value=[ElementTree.fromstring(self.covered_tr_html)]) line_coverage = self.parser.GetLineCoverage('fake_path') self.assertEqual(line_coverage[0].source, ' if (mSelectors.get(index) != null) {') def testGetLineCoverage_multipleElements(self): line_coverage = self.GetLineCoverageWithFakeElements( [self.covered_tr_html, self.partially_covered_tr_html, self.tr_with_extra_a_tag]) self.assertEqual(len(line_coverage), 3) def GetLineCoverageWithFakeElements(self, html_elements): """Wraps GetLineCoverage so mock HTML can easily be used. Args: html_elements: List of strings each representing an HTML element. Returns: A list of LineCoverage objects. """ elements = [ElementTree.fromstring(string) for string in html_elements] with mock.patch('emma_coverage_stats._EmmaHtmlParser._FindElements', return_value=elements): return self.parser.GetLineCoverage('fake_path') class _EmmaCoverageStatsTest(unittest.TestCase): """Tests for _EmmaCoverageStats.""" def setUp(self): self.good_source_to_emma = { '/path/to/1/File1.java': '/emma/1.html', '/path/2/File2.java': '/emma/2.html', '/path/2/File3.java': '/emma/3.html' } self.line_coverage = [ emma_coverage_stats.LineCoverage( 1, '', emma_coverage_stats.COVERED, 1.0), emma_coverage_stats.LineCoverage( 2, '', emma_coverage_stats.COVERED, 1.0), emma_coverage_stats.LineCoverage( 3, '', emma_coverage_stats.NOT_EXECUTABLE, 1.0), emma_coverage_stats.LineCoverage( 4, '', emma_coverage_stats.NOT_COVERED, 1.0), emma_coverage_stats.LineCoverage( 5, '', emma_coverage_stats.PARTIALLY_COVERED, 0.85), emma_coverage_stats.LineCoverage( 6, '', emma_coverage_stats.PARTIALLY_COVERED, 0.20) ] self.lines_for_coverage = [1, 3, 5, 6] with mock.patch('emma_coverage_stats._EmmaHtmlParser._FindElements', return_value=[]): self.simple_coverage = emma_coverage_stats._EmmaCoverageStats( 'fake_dir', {}) def testInit(self): coverage_stats = self.simple_coverage self.assertIsInstance(coverage_stats._emma_parser, emma_coverage_stats._EmmaHtmlParser) self.assertIsInstance(coverage_stats._source_to_emma, dict) def testNeedsCoverage_withExistingJavaFile(self): test_file = '/path/to/file/File.java' with mock.patch('os.path.exists', return_value=True): self.assertTrue( emma_coverage_stats._EmmaCoverageStats.NeedsCoverage(test_file)) def testNeedsCoverage_withNonJavaFile(self): test_file = '/path/to/file/File.c' with mock.patch('os.path.exists', return_value=True): self.assertFalse( emma_coverage_stats._EmmaCoverageStats.NeedsCoverage(test_file)) def testNeedsCoverage_fileDoesNotExist(self): test_file = '/path/to/file/File.java' with mock.patch('os.path.exists', return_value=False): self.assertFalse( emma_coverage_stats._EmmaCoverageStats.NeedsCoverage(test_file)) def testGetPackageNameFromFile_basic(self): test_file_text = """// Test Copyright package org.chromium.chrome.browser; import android.graphics.RectF;""" result_package, _ = MockOpenForFunction( emma_coverage_stats._EmmaCoverageStats.GetPackageNameFromFile, [test_file_text], file_path='/path/to/file/File.java') self.assertEqual(result_package, 'org.chromium.chrome.browser.File.java') def testGetPackageNameFromFile_noPackageStatement(self): result_package, _ = MockOpenForFunction( emma_coverage_stats._EmmaCoverageStats.GetPackageNameFromFile, ['not a package statement'], file_path='/path/to/file/File.java') self.assertIsNone(result_package) def testGetSummaryStatsForLines_basic(self): covered, total = self.simple_coverage.GetSummaryStatsForLines( self.line_coverage) self.assertEqual(covered, 3.05) self.assertEqual(total, 5) def testGetSourceFileToEmmaFileDict(self): package_names = { '/path/to/1/File1.java': 'org.fake.one.File1.java', '/path/2/File2.java': 'org.fake.File2.java', '/path/2/File3.java': 'org.fake.File3.java' } package_to_emma = { 'org.fake.one.File1.java': '/emma/1.html', 'org.fake.File2.java': '/emma/2.html', 'org.fake.File3.java': '/emma/3.html' } with mock.patch('os.path.exists', return_value=True): coverage_stats = self.simple_coverage coverage_stats._emma_parser.GetPackageNameToEmmaFileDict = mock.MagicMock( return_value=package_to_emma) coverage_stats.GetPackageNameFromFile = lambda x: package_names[x] result_dict = coverage_stats._GetSourceFileToEmmaFileDict( package_names.keys()) self.assertDictEqual(result_dict, self.good_source_to_emma) def testGetCoverageDictForFile(self): line_coverage = self.line_coverage self.simple_coverage._emma_parser.GetLineCoverage = lambda x: line_coverage self.simple_coverage._source_to_emma = {'/fake/src': 'fake/emma'} lines = self.lines_for_coverage expected_dict = { 'absolute': { 'covered': 3.05, 'total': 5 }, 'incremental': { 'covered': 2.05, 'total': 3 }, 'source': [ { 'line': line_coverage[0].source, 'coverage': line_coverage[0].covered_status, 'changed': True, 'fractional_coverage': line_coverage[0].fractional_line_coverage, }, { 'line': line_coverage[1].source, 'coverage': line_coverage[1].covered_status, 'changed': False, 'fractional_coverage': line_coverage[1].fractional_line_coverage, }, { 'line': line_coverage[2].source, 'coverage': line_coverage[2].covered_status, 'changed': True, 'fractional_coverage': line_coverage[2].fractional_line_coverage, }, { 'line': line_coverage[3].source, 'coverage': line_coverage[3].covered_status, 'changed': False, 'fractional_coverage': line_coverage[3].fractional_line_coverage, }, { 'line': line_coverage[4].source, 'coverage': line_coverage[4].covered_status, 'changed': True, 'fractional_coverage': line_coverage[4].fractional_line_coverage, }, { 'line': line_coverage[5].source, 'coverage': line_coverage[5].covered_status, 'changed': True, 'fractional_coverage': line_coverage[5].fractional_line_coverage, } ] } result_dict = self.simple_coverage.GetCoverageDictForFile( '/fake/src', lines) self.assertDictEqual(result_dict, expected_dict) def testGetCoverageDictForFile_emptyCoverage(self): expected_dict = { 'absolute': {'covered': 0, 'total': 0}, 'incremental': {'covered': 0, 'total': 0}, 'source': [] } self.simple_coverage._emma_parser.GetLineCoverage = lambda x: [] self.simple_coverage._source_to_emma = {'fake_dir': 'fake/emma'} result_dict = self.simple_coverage.GetCoverageDictForFile('fake_dir', {}) self.assertDictEqual(result_dict, expected_dict) def testGetCoverageDictForFile_missingCoverage(self): self.simple_coverage._source_to_emma = {} result_dict = self.simple_coverage.GetCoverageDictForFile('fake_file', {}) self.assertIsNone(result_dict) def testGetCoverageDict_basic(self): files_for_coverage = { '/path/to/1/File1.java': [1, 3, 4], '/path/2/File2.java': [1, 2] } self.simple_coverage._source_to_emma = { '/path/to/1/File1.java': 'emma_1', '/path/2/File2.java': 'emma_2' } coverage_info = { 'emma_1': [ emma_coverage_stats.LineCoverage( 1, '', emma_coverage_stats.COVERED, 1.0), emma_coverage_stats.LineCoverage( 2, '', emma_coverage_stats.PARTIALLY_COVERED, 0.5), emma_coverage_stats.LineCoverage( 3, '', emma_coverage_stats.NOT_EXECUTABLE, 1.0), emma_coverage_stats.LineCoverage( 4, '', emma_coverage_stats.COVERED, 1.0) ], 'emma_2': [ emma_coverage_stats.LineCoverage( 1, '', emma_coverage_stats.NOT_COVERED, 1.0), emma_coverage_stats.LineCoverage( 2, '', emma_coverage_stats.COVERED, 1.0) ] } expected_dict = { 'files': { '/path/2/File2.java': { 'absolute': {'covered': 1, 'total': 2}, 'incremental': {'covered': 1, 'total': 2}, 'source': [{'changed': True, 'coverage': 0, 'line': '', 'fractional_coverage': 1.0}, {'changed': True, 'coverage': 1, 'line': '', 'fractional_coverage': 1.0}] }, '/path/to/1/File1.java': { 'absolute': {'covered': 2.5, 'total': 3}, 'incremental': {'covered': 2, 'total': 2}, 'source': [{'changed': True, 'coverage': 1, 'line': '', 'fractional_coverage': 1.0}, {'changed': False, 'coverage': 2, 'line': '', 'fractional_coverage': 0.5}, {'changed': True, 'coverage': -1, 'line': '', 'fractional_coverage': 1.0}, {'changed': True, 'coverage': 1, 'line': '', 'fractional_coverage': 1.0}] } }, 'patch': {'incremental': {'covered': 3, 'total': 4}} } # Return the relevant coverage info for each file. self.simple_coverage._emma_parser.GetLineCoverage = ( lambda x: coverage_info[x]) result_dict = self.simple_coverage.GetCoverageDict(files_for_coverage) self.assertDictEqual(result_dict, expected_dict) def testGetCoverageDict_noCoverage(self): result_dict = self.simple_coverage.GetCoverageDict({}) self.assertDictEqual(result_dict, EMPTY_COVERAGE_STATS_DICT) class EmmaCoverageStatsGenerateCoverageReport(unittest.TestCase): """Tests for GenerateCoverageReport.""" def testGenerateCoverageReport_missingJsonFile(self): with self.assertRaises(IOError): with mock.patch('os.path.exists', return_value=False): emma_coverage_stats.GenerateCoverageReport('', '', '') def testGenerateCoverageReport_invalidJsonFile(self): with self.assertRaises(ValueError): with mock.patch('os.path.exists', return_value=True): MockOpenForFunction(emma_coverage_stats.GenerateCoverageReport, [''], line_coverage_file='', out_file_path='', coverage_dir='') def MockOpenForFunction(func, side_effects, **kwargs): """Allows easy mock open and read for callables that open multiple files. Will mock the python open function in a way such that each time read() is called on an open file, the next element in |side_effects| is returned. This makes it easier to test functions that call open() multiple times. Args: func: The callable to invoke once mock files are setup. side_effects: A list of return values for each file to return once read. Length of list should be equal to the number calls to open in |func|. **kwargs: Keyword arguments to be passed to |func|. Returns: A tuple containing the return value of |func| and the MagicMock object used to mock all calls to open respectively. """ mock_open = mock.mock_open() mock_open.side_effect = [mock.mock_open(read_data=side_effect).return_value for side_effect in side_effects] with mock.patch('__builtin__.open', mock_open): return func(**kwargs), mock_open if __name__ == '__main__': # Suppress logging messages. unittest.main(buffer=True)