|  | #!/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 = '<TR><TD CLASS="p">Test HTML</TD></TR>' | 
|  | self.index_html = ( | 
|  | '<HTML>' | 
|  | '<BODY>' | 
|  | '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">' | 
|  | '</TABLE>' | 
|  | '<TABLE CELLSPACING="0" WIDTH="100%">' | 
|  | '</TABLE>' | 
|  | '<TABLE CLASS="it" CELLSPACING="0">' | 
|  | '</TABLE>' | 
|  | '<TABLE CELLSPACING="0" WIDTH="100%">' | 
|  | '<TR>' | 
|  | '<TH CLASS="f">name</TH>' | 
|  | '<TH>class, %</TH>' | 
|  | '<TH>method, %</TH>' | 
|  | '<TH>block, %</TH>' | 
|  | '<TH>line, %</TH>' | 
|  | '</TR>' | 
|  | '<TR CLASS="o">' | 
|  | '<TD><A HREF="_files/0.html"' | 
|  | '>org.chromium.chrome.browser</A></TD>' | 
|  | '<TD CLASS="h">0%   (0/3)</TD>' | 
|  | '</TR>' | 
|  | '<TR>' | 
|  | '<TD><A HREF="_files/1.html"' | 
|  | '>org.chromium.chrome.browser.tabmodel</A></TD>' | 
|  | '<TD CLASS="h">0%   (0/8)</TD>' | 
|  | '</TR>' | 
|  | '</TABLE>' | 
|  | '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">' | 
|  | '</TABLE>' | 
|  | '</BODY>' | 
|  | '</HTML>' | 
|  | ) | 
|  | self.package_1_class_list_html = ( | 
|  | '<HTML>' | 
|  | '<BODY>' | 
|  | '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">' | 
|  | '</TABLE>' | 
|  | '<TABLE CELLSPACING="0" WIDTH="100%">' | 
|  | '</TABLE>' | 
|  | '<TABLE CELLSPACING="0" WIDTH="100%">' | 
|  | '<TR>' | 
|  | '<TH CLASS="f">name</TH>' | 
|  | '<TH>class, %</TH>' | 
|  | '<TH>method, %</TH>' | 
|  | '<TH>block, %</TH>' | 
|  | '<TH>line, %</TH>' | 
|  | '</TR>' | 
|  | '<TR CLASS="o">' | 
|  | '<TD><A HREF="1e.html">IntentHelper.java</A></TD>' | 
|  | '<TD CLASS="h">0%   (0/3)</TD>' | 
|  | '<TD CLASS="h">0%   (0/9)</TD>' | 
|  | '<TD CLASS="h">0%   (0/97)</TD>' | 
|  | '<TD CLASS="h">0%   (0/26)</TD>' | 
|  | '</TR>' | 
|  | '</TABLE>' | 
|  | '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">' | 
|  | '</TABLE>' | 
|  | '</BODY>' | 
|  | '</HTML>' | 
|  | ) | 
|  | self.package_2_class_list_html = ( | 
|  | '<HTML>' | 
|  | '<BODY>' | 
|  | '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">' | 
|  | '</TABLE>' | 
|  | '<TABLE CELLSPACING="0" WIDTH="100%">' | 
|  | '</TABLE>' | 
|  | '<TABLE CELLSPACING="0" WIDTH="100%">' | 
|  | '<TR>' | 
|  | '<TH CLASS="f">name</TH>' | 
|  | '<TH>class, %</TH>' | 
|  | '<TH>method, %</TH>' | 
|  | '<TH>block, %</TH>' | 
|  | '<TH>line, %</TH>' | 
|  | '</TR>' | 
|  | '<TR CLASS="o">' | 
|  | '<TD><A HREF="1f.html">ContentSetting.java</A></TD>' | 
|  | '<TD CLASS="h">0%   (0/1)</TD>' | 
|  | '</TR>' | 
|  | '<TR>' | 
|  | '<TD><A HREF="20.html">DevToolsServer.java</A></TD>' | 
|  | '</TR>' | 
|  | '<TR CLASS="o">' | 
|  | '<TD><A HREF="21.html">FileProviderHelper.java</A></TD>' | 
|  | '</TR>' | 
|  | '<TR>' | 
|  | '<TD><A HREF="22.html">ContextualMenuBar.java</A></TD>' | 
|  | '</TR>' | 
|  | '<TR CLASS="o">' | 
|  | '<TD><A HREF="23.html">AccessibilityUtil.java</A></TD>' | 
|  | '</TR>' | 
|  | '<TR>' | 
|  | '<TD><A HREF="24.html">NavigationPopup.java</A></TD>' | 
|  | '</TR>' | 
|  | '</TABLE>' | 
|  | '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">' | 
|  | '</TABLE>' | 
|  | '</BODY>' | 
|  | '</HTML>' | 
|  | ) | 
|  | self.partially_covered_tr_html = ( | 
|  | '<TR CLASS="p">' | 
|  | '<TD CLASS="l" TITLE="78% line coverage (7 out of 9)">108</TD>' | 
|  | '<TD TITLE="78% line coverage (7 out of 9 instructions)">' | 
|  | 'if (index < 0 || index = mSelectors.size()) index = 0;</TD>' | 
|  | '</TR>' | 
|  | ) | 
|  | self.covered_tr_html = ( | 
|  | '<TR CLASS="c">' | 
|  | '<TD CLASS="l">110</TD>' | 
|  | '<TD>        if (mSelectors.get(index) != null) {</TD>' | 
|  | '</TR>' | 
|  | ) | 
|  | self.not_executable_tr_html = ( | 
|  | '<TR>' | 
|  | '<TD CLASS="l">109</TD>' | 
|  | '<TD> </TD>' | 
|  | '</TR>' | 
|  | ) | 
|  | self.tr_with_extra_a_tag = ( | 
|  | '<TR CLASS="z">' | 
|  | '<TD CLASS="l">' | 
|  | '<A name="1f">54</A>' | 
|  | '</TD>' | 
|  | '<TD>            }</TD>' | 
|  | '</TR>' | 
|  | ) | 
|  |  | 
|  | 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 = ['<div>' + multiple_trs + '</div>'] | 
|  | 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) |