Index: build/android/coverage_test.py |
diff --git a/build/android/coverage_test.py b/build/android/coverage_test.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..c9551b6291f2211941ff8f91e26d7d6614845c28 |
--- /dev/null |
+++ b/build/android/coverage_test.py |
@@ -0,0 +1,558 @@ |
+#!/usr/bin/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. |
+ |
+from lxml import etree |
+from lxml import html |
+import os |
+import sys |
+import unittest |
+ |
+import coverage |
+from pylib import constants |
+ |
+sys.path.append(os.path.join( |
+ constants.DIR_SOURCE_ROOT, 'third_party', 'pymock')) |
+import mock # pylint: disable=F0401 |
+ |
+ |
+class LineCoverageTest(unittest.TestCase): |
+ """Tests for LineCoverage.""" |
+ |
+ def testInit(self): |
+ line_coverage = coverage.LineCoverage(10, 'i++;', |
+ coverage.LineCoverage.COVERED, 1.0) |
+ self.assertEqual(line_coverage.lineno, 10) |
+ self.assertEqual(line_coverage.source, 'i++;') |
+ self.assertEqual(line_coverage.covered_status, |
+ coverage.LineCoverage.COVERED) |
+ self.assertEqual(line_coverage.fractional_line_coverage, 1.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 = coverage._EmmaHtmlParser(self.emma_dir) |
+ self.simple_html = '<td class="p">Test HTML</td>' |
+ 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 = coverage._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): |
+ found, _ = MockOpenForFunction(self.parser._FindElements, self.simple_html, |
+ file_path='fake', _path='//td') |
+ self.assertIs(type(found), list) |
+ self.assertIs(type(found[0]), html.HtmlElement) |
+ self.assertEqual(found[0].text, 'Test HTML') |
+ |
+ def testFindElements_noMatch(self): |
+ found, _ = MockOpenForFunction(self.parser._FindElements, self.simple_html, |
+ file_path='fake', _path='//tr') |
+ self.assertEqual(found, []) |
+ |
+ def testFindElements_badFilePath(self): |
+ # Ensure that os.path.exists will fail. |
+ with mock.patch('os.path.exists', return_value=False): |
+ found = self.parser._FindElements('fake', _path='//tr') |
+ self.assertEqual(found, []) |
+ |
+ 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'} |
+ |
+ return_dict, mock_open = MockOpenForFunction( |
+ self.parser.GetPackageNameToEmmaFileDict, |
+ self.index_html, self.package_1_class_list_html, |
+ self.package_2_class_list_html) |
+ |
+ 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_badFilePath(self): |
+ file_path = self.parser.GetPackageNameToEmmaFileDict() |
+ self.assertEqual(file_path, {}) |
+ |
+ def testGetLineCoverage_status_basic(self): |
+ line_coverage = self.GetLineCoverageWithFakeElements(self.covered_tr_html) |
+ self.assertEqual(line_coverage[0].covered_status, |
+ coverage.LineCoverage.COVERED) |
+ |
+ def testGetLineCoverage_status_statusMissing(self): |
+ line_coverage = self.GetLineCoverageWithFakeElements( |
+ self.not_executable_tr_html) |
+ self.assertEqual(line_coverage[0].covered_status, |
+ coverage.LineCoverage.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=[html.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, *args): |
+ """Wraps GetLineCoverage to work with extra whitespace characters. |
+ |
+ The test HTML strings include extra whitespace characters to make the HTML |
+ human readable. This isn't the case with EMMA HTML files, so we need to |
+ remove all the unnecessary whitespace. |
+ |
+ Args: |
+ *args: Each is a string representing an HTML element. |
+ |
+ Returns: |
+ A list of LineCoverage objects. |
+ """ |
+ html_elements = [self.MakeElementsWithoutWhitespace(arg) for arg in args] |
+ with mock.patch('coverage._EmmaHtmlParser._FindElements', |
+ return_value=html_elements): |
+ return self.parser.GetLineCoverage('fake_path') |
+ |
+ def MakeElementsWithoutWhitespace(self, html_string): |
+ """Helper to make HtmlElements with excess whitespace removed. |
+ |
+ Args: |
+ html_string: String representing HTML to be used to create elements. |
+ |
+ Returns: |
+ The root element of |html_string| with all extra whitespace removed. |
+ """ |
+ parser = etree.HTMLParser(remove_blank_text=True) |
+ root = html.fromstring(html_string, parser=parser) |
+ for elem in root.iter('*'): |
+ if elem.text is not None: |
+ elem.text = elem.text.strip() |
+ return root |
+ |
+ |
+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 = [ |
+ coverage.LineCoverage(1, '', coverage.LineCoverage.COVERED, 1.0), |
+ coverage.LineCoverage(2, '', coverage.LineCoverage.COVERED, 1.0), |
+ coverage.LineCoverage(3, '', coverage.LineCoverage.NOT_EXECUTABLE, 1.0), |
+ coverage.LineCoverage(4, '', coverage.LineCoverage.NOT_COVERED, 1.0), |
+ coverage.LineCoverage(5, '', |
+ coverage.LineCoverage.PARTIALLY_COVERED, 0.85), |
+ coverage.LineCoverage(6, '', |
+ coverage.LineCoverage.PARTIALLY_COVERED, 0.20) |
+ ] |
+ self.lines_for_coverage = [1, 3, 5, 6] |
+ self.simple_coverage = coverage._EmmaCoverageStats('fake_dir', {}) |
+ |
+ def testInit(self): |
+ coverage_stats = self.simple_coverage |
+ self.assertIsInstance(coverage_stats._emma_parser, |
+ coverage._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(coverage._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(coverage._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(coverage._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( |
+ coverage._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( |
+ coverage._EmmaCoverageStats.GetPackageNameFromFile, |
+ 'not a package statement', file_path='/path/to/file/File.java') |
+ self.assertEqual(result_package, None) |
+ |
+ def testGetStatsForLines_basic(self): |
+ covered, total = self.simple_coverage._GetStatsForLines( |
+ self.line_coverage, self.lines_for_coverage) |
+ self.assertEqual(covered, 2.05) |
+ self.assertEqual(total, 3) |
+ |
+ def testGetStatsForLines_noLineNumbersProvided(self): |
+ covered, total = self.simple_coverage._GetStatsForLines(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 testGetCoverageStatusFromFile_basic(self): |
+ java_file_path = '/path/to/1/File1.java' |
+ line_coverage = [ |
+ coverage.LineCoverage(1, '', coverage.LineCoverage.COVERED, 1.0)] |
+ coverage_stats = self.simple_coverage |
+ coverage_stats._source_to_emma = self.good_source_to_emma |
+ coverage_stats._emma_parser.GetLineCoverage = mock.MagicMock( |
+ return_value=line_coverage) |
+ coverage_info = coverage_stats._GetCoverageStatusForFile(java_file_path) |
+ self.assertDictEqual(coverage_info[0].__dict__, line_coverage[0].__dict__) |
+ |
+ def testGetCoverageStatusFromFile_noInfo(self): |
+ coverage_info = self.simple_coverage._GetCoverageStatusForFile('fake_path') |
+ self.assertIsNone(coverage_info) |
+ |
+ def testGetCoverageReportForLines(self): |
+ line_coverage = self.line_coverage |
+ 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 |
+ }, |
+ { |
+ 'line': line_coverage[1].source, |
+ 'coverage': line_coverage[1].covered_status, |
+ 'changed': False |
+ }, |
+ { |
+ 'line': line_coverage[2].source, |
+ 'coverage': line_coverage[2].covered_status, |
+ 'changed': True |
+ }, |
+ { |
+ 'line': line_coverage[3].source, |
+ 'coverage': line_coverage[3].covered_status, |
+ 'changed': False |
+ }, |
+ { |
+ 'line': line_coverage[4].source, |
+ 'coverage': line_coverage[4].covered_status, |
+ 'changed': True |
+ }, |
+ { |
+ 'line': line_coverage[5].source, |
+ 'coverage': line_coverage[5].covered_status, |
+ 'changed': True |
+ } |
+ ] |
+ } |
+ result_dict = self.simple_coverage.GetCoverageReportForLines(line_coverage, |
+ lines) |
+ self.assertDictEqual(result_dict, expected_dict) |
+ |
+ def testGetCoverageReportForLines_emptyCoverage(self): |
+ expected_dict = { |
+ 'absolute': {'covered': 0, 'total': 0}, |
+ 'incremental': {'covered': 0, 'total': 0}, |
+ 'source': [] |
+ } |
+ result_dict = self.simple_coverage.GetCoverageReportForLines({}, []) |
+ self.assertDictEqual(result_dict, expected_dict) |
+ |
+ def testGetCoverageDictForFiles_basic(self): |
+ files_for_coverage = { |
+ '/path/to/1/File1.java': [1, 3, 4], |
+ '/path/2/File2.java': [1, 2] |
+ } |
+ coverage_info = { |
+ '/path/to/1/File1.java': [ |
+ coverage.LineCoverage(1, '', coverage.LineCoverage.COVERED, 1.0), |
+ coverage.LineCoverage(2, '', |
+ coverage.LineCoverage.PARTIALLY_COVERED, 0.5), |
+ coverage.LineCoverage(3, '', |
+ coverage.LineCoverage.NOT_EXECUTABLE, 1.0), |
+ coverage.LineCoverage(4, '', coverage.LineCoverage.COVERED, 1.0) |
+ ], |
+ '/path/2/File2.java': [ |
+ coverage.LineCoverage(1, '', coverage.LineCoverage.NOT_COVERED, 1.0), |
+ coverage.LineCoverage(2, '', coverage.LineCoverage.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': ''}, |
+ {'changed': True, 'coverage': 1, 'line': ''}] |
+ }, |
+ '/path/to/1/File1.java': { |
+ 'absolute': {'covered': 2.5, 'total': 3}, |
+ 'incremental': {'covered': 2, 'total': 2}, |
+ 'source': [{'changed': True, 'coverage': 1, 'line': ''}, |
+ {'changed': False, 'coverage': 2, 'line': ''}, |
+ {'changed': True, 'coverage': -1, 'line': ''}, |
+ {'changed': True, 'coverage': 1, 'line': ''}] |
+ } |
+ }, |
+ 'patch': {'incremental': {'covered': 3, 'total': 4}} |
+ } |
+ self.simple_coverage._GetCoverageStatusForFile = lambda x: coverage_info[x] |
+ result_dict = self.simple_coverage.GetCoverageDictForFiles( |
+ files_for_coverage) |
+ self.assertDictEqual(result_dict, expected_dict) |
+ |
+ def testGetCoverageDictForFiles_noCoverage(self): |
+ expected_dict = { |
+ 'files': {}, |
+ 'patch': { |
+ 'incremental': { |
+ 'covered': 0, 'total': 0 |
+ } |
+ } |
+ } |
+ result_dict = self.simple_coverage.GetCoverageDictForFiles({}) |
+ self.assertDictEqual(result_dict, expected_dict) |
+ |
+ |
+class CoverageGenerateCoverageReportTest(unittest.TestCase): |
+ """Tests for GenerateCoverageReport.""" |
+ |
+ def testGenerateCoverageReport_missingLineCoverageFile(self): |
+ with self.assertRaises(IOError): |
+ with mock.patch('os.path.exists', return_value=False): |
+ coverage.GenerateCoverageReport('', '', '') |
+ |
+ def testGenerateCoverageReport_noCoverageRequired(self): |
+ with self.assertRaises(SystemExit) as cm: |
+ with mock.patch('os.path.exists', return_value=True): |
+ MockOpenForFunction(coverage.GenerateCoverageReport, '{}', |
+ line_coverage_file='', out_file_path='', |
+ coverage_dir='') |
+ self.assertEqual(cm.exception.code, 0) |
+ |
+ |
+def MockOpenForFunction(func, *args, **kwargs): |
+ """Allows easy mock open and read for callables that open multiple files. |
+ |
+ Args: |
+ func: The callable to invoke once mock files are setup. |
+ *args: 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=arg).return_value |
+ for arg in args] |
+ with mock.patch('__builtin__.open', mock_open): |
+ return func(**kwargs), mock_open |
+ |
+ |
+if __name__ == '__main__': |
+ # Suppress logging messages. |
+ unittest.main(buffer=True) |