OLD | NEW |
(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 < 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) |
OLD | NEW |