emma_coverage_stats_test.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. #!/usr/bin/env vpython3
  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. # pylint: disable=protected-access
  6. import sys
  7. import unittest
  8. from xml.etree import ElementTree
  9. import emma_coverage_stats
  10. import mock # pylint: disable=import-error
  11. EMPTY_COVERAGE_STATS_DICT = {
  12. 'files': {},
  13. 'patch': {
  14. 'incremental': {
  15. 'covered': 0, 'total': 0
  16. }
  17. }
  18. }
  19. class _EmmaHtmlParserTest(unittest.TestCase):
  20. """Tests for _EmmaHtmlParser.
  21. Uses modified EMMA report HTML that contains only the subset of tags needed
  22. for test verification.
  23. """
  24. def setUp(self):
  25. self.emma_dir = 'fake/dir/'
  26. self.parser = emma_coverage_stats._EmmaHtmlParser(self.emma_dir)
  27. self.simple_html = '<TR><TD CLASS="p">Test HTML</TD></TR>'
  28. self.index_html = (
  29. '<HTML>'
  30. '<BODY>'
  31. '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
  32. '</TABLE>'
  33. '<TABLE CELLSPACING="0" WIDTH="100%">'
  34. '</TABLE>'
  35. '<TABLE CLASS="it" CELLSPACING="0">'
  36. '</TABLE>'
  37. '<TABLE CELLSPACING="0" WIDTH="100%">'
  38. '<TR>'
  39. '<TH CLASS="f">name</TH>'
  40. '<TH>class, %</TH>'
  41. '<TH>method, %</TH>'
  42. '<TH>block, %</TH>'
  43. '<TH>line, %</TH>'
  44. '</TR>'
  45. '<TR CLASS="o">'
  46. '<TD><A HREF="_files/0.html"'
  47. '>org.chromium.chrome.browser</A></TD>'
  48. '<TD CLASS="h">0% (0/3)</TD>'
  49. '</TR>'
  50. '<TR>'
  51. '<TD><A HREF="_files/1.html"'
  52. '>org.chromium.chrome.browser.tabmodel</A></TD>'
  53. '<TD CLASS="h">0% (0/8)</TD>'
  54. '</TR>'
  55. '</TABLE>'
  56. '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
  57. '</TABLE>'
  58. '</BODY>'
  59. '</HTML>'
  60. )
  61. self.package_1_class_list_html = (
  62. '<HTML>'
  63. '<BODY>'
  64. '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
  65. '</TABLE>'
  66. '<TABLE CELLSPACING="0" WIDTH="100%">'
  67. '</TABLE>'
  68. '<TABLE CELLSPACING="0" WIDTH="100%">'
  69. '<TR>'
  70. '<TH CLASS="f">name</TH>'
  71. '<TH>class, %</TH>'
  72. '<TH>method, %</TH>'
  73. '<TH>block, %</TH>'
  74. '<TH>line, %</TH>'
  75. '</TR>'
  76. '<TR CLASS="o">'
  77. '<TD><A HREF="1e.html">IntentHelper.java</A></TD>'
  78. '<TD CLASS="h">0% (0/3)</TD>'
  79. '<TD CLASS="h">0% (0/9)</TD>'
  80. '<TD CLASS="h">0% (0/97)</TD>'
  81. '<TD CLASS="h">0% (0/26)</TD>'
  82. '</TR>'
  83. '</TABLE>'
  84. '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
  85. '</TABLE>'
  86. '</BODY>'
  87. '</HTML>'
  88. )
  89. self.package_2_class_list_html = (
  90. '<HTML>'
  91. '<BODY>'
  92. '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
  93. '</TABLE>'
  94. '<TABLE CELLSPACING="0" WIDTH="100%">'
  95. '</TABLE>'
  96. '<TABLE CELLSPACING="0" WIDTH="100%">'
  97. '<TR>'
  98. '<TH CLASS="f">name</TH>'
  99. '<TH>class, %</TH>'
  100. '<TH>method, %</TH>'
  101. '<TH>block, %</TH>'
  102. '<TH>line, %</TH>'
  103. '</TR>'
  104. '<TR CLASS="o">'
  105. '<TD><A HREF="1f.html">ContentSetting.java</A></TD>'
  106. '<TD CLASS="h">0% (0/1)</TD>'
  107. '</TR>'
  108. '<TR>'
  109. '<TD><A HREF="20.html">DevToolsServer.java</A></TD>'
  110. '</TR>'
  111. '<TR CLASS="o">'
  112. '<TD><A HREF="21.html">FileProviderHelper.java</A></TD>'
  113. '</TR>'
  114. '<TR>'
  115. '<TD><A HREF="22.html">ContextualMenuBar.java</A></TD>'
  116. '</TR>'
  117. '<TR CLASS="o">'
  118. '<TD><A HREF="23.html">AccessibilityUtil.java</A></TD>'
  119. '</TR>'
  120. '<TR>'
  121. '<TD><A HREF="24.html">NavigationPopup.java</A></TD>'
  122. '</TR>'
  123. '</TABLE>'
  124. '<TABLE CLASS="hdft" CELLSPACING="0" WIDTH="100%">'
  125. '</TABLE>'
  126. '</BODY>'
  127. '</HTML>'
  128. )
  129. self.partially_covered_tr_html = (
  130. '<TR CLASS="p">'
  131. '<TD CLASS="l" TITLE="78% line coverage (7 out of 9)">108</TD>'
  132. '<TD TITLE="78% line coverage (7 out of 9 instructions)">'
  133. 'if (index &lt; 0 || index = mSelectors.size()) index = 0;</TD>'
  134. '</TR>'
  135. )
  136. self.covered_tr_html = (
  137. '<TR CLASS="c">'
  138. '<TD CLASS="l">110</TD>'
  139. '<TD> if (mSelectors.get(index) != null) {</TD>'
  140. '</TR>'
  141. )
  142. self.not_executable_tr_html = (
  143. '<TR>'
  144. '<TD CLASS="l">109</TD>'
  145. '<TD> </TD>'
  146. '</TR>'
  147. )
  148. self.tr_with_extra_a_tag = (
  149. '<TR CLASS="z">'
  150. '<TD CLASS="l">'
  151. '<A name="1f">54</A>'
  152. '</TD>'
  153. '<TD> }</TD>'
  154. '</TR>'
  155. )
  156. def testInit(self):
  157. emma_dir = self.emma_dir
  158. parser = emma_coverage_stats._EmmaHtmlParser(emma_dir)
  159. self.assertEqual(parser._base_dir, emma_dir)
  160. self.assertEqual(parser._emma_files_path, 'fake/dir/_files')
  161. self.assertEqual(parser._index_path, 'fake/dir/index.html')
  162. def testFindElements_basic(self):
  163. read_values = [self.simple_html]
  164. found, _ = MockOpenForFunction(self.parser._FindElements, read_values,
  165. file_path='fake', xpath_selector='.//TD')
  166. self.assertIs(type(found), list)
  167. self.assertIs(type(found[0]), ElementTree.Element)
  168. self.assertEqual(found[0].text, 'Test HTML')
  169. def testFindElements_multipleElements(self):
  170. multiple_trs = self.not_executable_tr_html + self.covered_tr_html
  171. read_values = ['<div>' + multiple_trs + '</div>']
  172. found, _ = MockOpenForFunction(self.parser._FindElements, read_values,
  173. file_path='fake', xpath_selector='.//TR')
  174. self.assertEqual(2, len(found))
  175. def testFindElements_noMatch(self):
  176. read_values = [self.simple_html]
  177. found, _ = MockOpenForFunction(self.parser._FindElements, read_values,
  178. file_path='fake', xpath_selector='.//TR')
  179. self.assertEqual(found, [])
  180. def testFindElements_badFilePath(self):
  181. with self.assertRaises(IOError):
  182. with mock.patch('os.path.exists', return_value=False):
  183. self.parser._FindElements('fake', xpath_selector='//tr')
  184. def testGetPackageNameToEmmaFileDict_basic(self):
  185. if sys.version_info.major < 3:
  186. expected_dict = {
  187. 'org.chromium.chrome.browser.AccessibilityUtil.java':
  188. 'fake/dir/_files/23.html',
  189. 'org.chromium.chrome.browser.ContextualMenuBar.java':
  190. 'fake/dir/_files/22.html',
  191. 'org.chromium.chrome.browser.tabmodel.IntentHelper.java':
  192. 'fake/dir/_files/1e.html',
  193. 'org.chromium.chrome.browser.ContentSetting.java':
  194. 'fake/dir/_files/1f.html',
  195. 'org.chromium.chrome.browser.DevToolsServer.java':
  196. 'fake/dir/_files/20.html',
  197. 'org.chromium.chrome.browser.NavigationPopup.java':
  198. 'fake/dir/_files/24.html',
  199. 'org.chromium.chrome.browser.FileProviderHelper.java':
  200. 'fake/dir/_files/21.html'
  201. }
  202. else:
  203. expected_dict = {
  204. 'org.chromium.chrome.browser.IntentHelper.java':
  205. 'fake/dir/_files/1e.html',
  206. 'org.chromium.chrome.browser.tabmodel.AccessibilityUtil.java':
  207. 'fake/dir/_files/23.html',
  208. 'org.chromium.chrome.browser.tabmodel.ContextualMenuBar.java':
  209. 'fake/dir/_files/22.html',
  210. 'org.chromium.chrome.browser.tabmodel.ContentSetting.java':
  211. 'fake/dir/_files/1f.html',
  212. 'org.chromium.chrome.browser.tabmodel.DevToolsServer.java':
  213. 'fake/dir/_files/20.html',
  214. 'org.chromium.chrome.browser.tabmodel.NavigationPopup.java':
  215. 'fake/dir/_files/24.html',
  216. 'org.chromium.chrome.browser.tabmodel.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. self.assertDictEqual(return_dict, expected_dict)
  224. self.assertEqual(mock_open.call_count, 3)
  225. if sys.version_info.major < 3:
  226. calls = [
  227. mock.call('fake/dir/index.html'),
  228. mock.call('fake/dir/_files/1.html'),
  229. mock.call('fake/dir/_files/0.html')
  230. ]
  231. else:
  232. calls = [
  233. mock.call('fake/dir/index.html'),
  234. mock.call('fake/dir/_files/0.html'),
  235. mock.call('fake/dir/_files/1.html')
  236. ]
  237. mock_open.assert_has_calls(calls)
  238. def testGetPackageNameToEmmaFileDict_noPackageElements(self):
  239. self.parser._FindElements = mock.Mock(return_value=[])
  240. return_dict = self.parser.GetPackageNameToEmmaFileDict()
  241. self.assertDictEqual({}, return_dict)
  242. def testGetLineCoverage_status_basic(self):
  243. line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html])
  244. self.assertEqual(line_coverage[0].covered_status,
  245. emma_coverage_stats.COVERED)
  246. def testGetLineCoverage_status_statusMissing(self):
  247. line_coverage = self.GetLineCoverageWithFakeElements(
  248. [self.not_executable_tr_html])
  249. self.assertEqual(line_coverage[0].covered_status,
  250. emma_coverage_stats.NOT_EXECUTABLE)
  251. def testGetLineCoverage_fractionalCoverage_basic(self):
  252. line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html])
  253. self.assertEqual(line_coverage[0].fractional_line_coverage, 1.0)
  254. def testGetLineCoverage_fractionalCoverage_partial(self):
  255. line_coverage = self.GetLineCoverageWithFakeElements(
  256. [self.partially_covered_tr_html])
  257. self.assertEqual(line_coverage[0].fractional_line_coverage, 0.78)
  258. def testGetLineCoverage_lineno_basic(self):
  259. line_coverage = self.GetLineCoverageWithFakeElements([self.covered_tr_html])
  260. self.assertEqual(line_coverage[0].lineno, 110)
  261. def testGetLineCoverage_lineno_withAlternativeHtml(self):
  262. line_coverage = self.GetLineCoverageWithFakeElements(
  263. [self.tr_with_extra_a_tag])
  264. self.assertEqual(line_coverage[0].lineno, 54)
  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. def testGetLineCoverage_multipleElements(self):
  272. line_coverage = self.GetLineCoverageWithFakeElements(
  273. [self.covered_tr_html, self.partially_covered_tr_html,
  274. self.tr_with_extra_a_tag])
  275. self.assertEqual(len(line_coverage), 3)
  276. def GetLineCoverageWithFakeElements(self, html_elements):
  277. """Wraps GetLineCoverage so mock HTML can easily be used.
  278. Args:
  279. html_elements: List of strings each representing an HTML element.
  280. Returns:
  281. A list of LineCoverage objects.
  282. """
  283. elements = [ElementTree.fromstring(string) for string in html_elements]
  284. with mock.patch('emma_coverage_stats._EmmaHtmlParser._FindElements',
  285. return_value=elements):
  286. return self.parser.GetLineCoverage('fake_path')
  287. class _EmmaCoverageStatsTest(unittest.TestCase):
  288. """Tests for _EmmaCoverageStats."""
  289. def setUp(self):
  290. self.good_source_to_emma = {
  291. '/path/to/1/File1.java': '/emma/1.html',
  292. '/path/2/File2.java': '/emma/2.html',
  293. '/path/2/File3.java': '/emma/3.html'
  294. }
  295. self.line_coverage = [
  296. emma_coverage_stats.LineCoverage(
  297. 1, '', emma_coverage_stats.COVERED, 1.0),
  298. emma_coverage_stats.LineCoverage(
  299. 2, '', emma_coverage_stats.COVERED, 1.0),
  300. emma_coverage_stats.LineCoverage(
  301. 3, '', emma_coverage_stats.NOT_EXECUTABLE, 1.0),
  302. emma_coverage_stats.LineCoverage(
  303. 4, '', emma_coverage_stats.NOT_COVERED, 1.0),
  304. emma_coverage_stats.LineCoverage(
  305. 5, '', emma_coverage_stats.PARTIALLY_COVERED, 0.85),
  306. emma_coverage_stats.LineCoverage(
  307. 6, '', emma_coverage_stats.PARTIALLY_COVERED, 0.20)
  308. ]
  309. self.lines_for_coverage = [1, 3, 5, 6]
  310. with mock.patch('emma_coverage_stats._EmmaHtmlParser._FindElements',
  311. return_value=[]):
  312. self.simple_coverage = emma_coverage_stats._EmmaCoverageStats(
  313. 'fake_dir', {})
  314. def testInit(self):
  315. coverage_stats = self.simple_coverage
  316. self.assertIsInstance(coverage_stats._emma_parser,
  317. emma_coverage_stats._EmmaHtmlParser)
  318. self.assertIsInstance(coverage_stats._source_to_emma, dict)
  319. def testNeedsCoverage_withExistingJavaFile(self):
  320. test_file = '/path/to/file/File.java'
  321. with mock.patch('os.path.exists', return_value=True):
  322. self.assertTrue(
  323. emma_coverage_stats._EmmaCoverageStats.NeedsCoverage(test_file))
  324. def testNeedsCoverage_withNonJavaFile(self):
  325. test_file = '/path/to/file/File.c'
  326. with mock.patch('os.path.exists', return_value=True):
  327. self.assertFalse(
  328. emma_coverage_stats._EmmaCoverageStats.NeedsCoverage(test_file))
  329. def testNeedsCoverage_fileDoesNotExist(self):
  330. test_file = '/path/to/file/File.java'
  331. with mock.patch('os.path.exists', return_value=False):
  332. self.assertFalse(
  333. emma_coverage_stats._EmmaCoverageStats.NeedsCoverage(test_file))
  334. def testGetPackageNameFromFile_basic(self):
  335. test_file_text = """// Test Copyright
  336. package org.chromium.chrome.browser;
  337. import android.graphics.RectF;"""
  338. result_package, _ = MockOpenForFunction(
  339. emma_coverage_stats._EmmaCoverageStats.GetPackageNameFromFile,
  340. [test_file_text], file_path='/path/to/file/File.java')
  341. self.assertEqual(result_package, 'org.chromium.chrome.browser.File.java')
  342. def testGetPackageNameFromFile_noPackageStatement(self):
  343. result_package, _ = MockOpenForFunction(
  344. emma_coverage_stats._EmmaCoverageStats.GetPackageNameFromFile,
  345. ['not a package statement'], file_path='/path/to/file/File.java')
  346. self.assertIsNone(result_package)
  347. def testGetSummaryStatsForLines_basic(self):
  348. covered, total = self.simple_coverage.GetSummaryStatsForLines(
  349. self.line_coverage)
  350. self.assertEqual(covered, 3.05)
  351. self.assertEqual(total, 5)
  352. def testGetSourceFileToEmmaFileDict(self):
  353. package_names = {
  354. '/path/to/1/File1.java': 'org.fake.one.File1.java',
  355. '/path/2/File2.java': 'org.fake.File2.java',
  356. '/path/2/File3.java': 'org.fake.File3.java'
  357. }
  358. package_to_emma = {
  359. 'org.fake.one.File1.java': '/emma/1.html',
  360. 'org.fake.File2.java': '/emma/2.html',
  361. 'org.fake.File3.java': '/emma/3.html'
  362. }
  363. with mock.patch('os.path.exists', return_value=True):
  364. coverage_stats = self.simple_coverage
  365. coverage_stats._emma_parser.GetPackageNameToEmmaFileDict = mock.MagicMock(
  366. return_value=package_to_emma)
  367. coverage_stats.GetPackageNameFromFile = lambda x: package_names[x]
  368. result_dict = coverage_stats._GetSourceFileToEmmaFileDict(
  369. list(package_names.keys()))
  370. self.assertDictEqual(result_dict, self.good_source_to_emma)
  371. def testGetCoverageDictForFile(self):
  372. line_coverage = self.line_coverage
  373. self.simple_coverage._emma_parser.GetLineCoverage = lambda x: line_coverage
  374. self.simple_coverage._source_to_emma = {'/fake/src': 'fake/emma'}
  375. lines = self.lines_for_coverage
  376. expected_dict = {
  377. 'absolute': {
  378. 'covered': 3.05,
  379. 'total': 5
  380. },
  381. 'incremental': {
  382. 'covered': 2.05,
  383. 'total': 3
  384. },
  385. 'source': [
  386. {
  387. 'line': line_coverage[0].source,
  388. 'coverage': line_coverage[0].covered_status,
  389. 'changed': True,
  390. 'fractional_coverage': line_coverage[0].fractional_line_coverage,
  391. },
  392. {
  393. 'line': line_coverage[1].source,
  394. 'coverage': line_coverage[1].covered_status,
  395. 'changed': False,
  396. 'fractional_coverage': line_coverage[1].fractional_line_coverage,
  397. },
  398. {
  399. 'line': line_coverage[2].source,
  400. 'coverage': line_coverage[2].covered_status,
  401. 'changed': True,
  402. 'fractional_coverage': line_coverage[2].fractional_line_coverage,
  403. },
  404. {
  405. 'line': line_coverage[3].source,
  406. 'coverage': line_coverage[3].covered_status,
  407. 'changed': False,
  408. 'fractional_coverage': line_coverage[3].fractional_line_coverage,
  409. },
  410. {
  411. 'line': line_coverage[4].source,
  412. 'coverage': line_coverage[4].covered_status,
  413. 'changed': True,
  414. 'fractional_coverage': line_coverage[4].fractional_line_coverage,
  415. },
  416. {
  417. 'line': line_coverage[5].source,
  418. 'coverage': line_coverage[5].covered_status,
  419. 'changed': True,
  420. 'fractional_coverage': line_coverage[5].fractional_line_coverage,
  421. }
  422. ]
  423. }
  424. result_dict = self.simple_coverage.GetCoverageDictForFile(
  425. '/fake/src', lines)
  426. self.assertDictEqual(result_dict, expected_dict)
  427. def testGetCoverageDictForFile_emptyCoverage(self):
  428. expected_dict = {
  429. 'absolute': {'covered': 0, 'total': 0},
  430. 'incremental': {'covered': 0, 'total': 0},
  431. 'source': []
  432. }
  433. self.simple_coverage._emma_parser.GetLineCoverage = lambda x: []
  434. self.simple_coverage._source_to_emma = {'fake_dir': 'fake/emma'}
  435. result_dict = self.simple_coverage.GetCoverageDictForFile('fake_dir', {})
  436. self.assertDictEqual(result_dict, expected_dict)
  437. def testGetCoverageDictForFile_missingCoverage(self):
  438. self.simple_coverage._source_to_emma = {}
  439. result_dict = self.simple_coverage.GetCoverageDictForFile('fake_file', {})
  440. self.assertIsNone(result_dict)
  441. def testGetCoverageDict_basic(self):
  442. files_for_coverage = {
  443. '/path/to/1/File1.java': [1, 3, 4],
  444. '/path/2/File2.java': [1, 2]
  445. }
  446. self.simple_coverage._source_to_emma = {
  447. '/path/to/1/File1.java': 'emma_1',
  448. '/path/2/File2.java': 'emma_2'
  449. }
  450. coverage_info = {
  451. 'emma_1': [
  452. emma_coverage_stats.LineCoverage(
  453. 1, '', emma_coverage_stats.COVERED, 1.0),
  454. emma_coverage_stats.LineCoverage(
  455. 2, '', emma_coverage_stats.PARTIALLY_COVERED, 0.5),
  456. emma_coverage_stats.LineCoverage(
  457. 3, '', emma_coverage_stats.NOT_EXECUTABLE, 1.0),
  458. emma_coverage_stats.LineCoverage(
  459. 4, '', emma_coverage_stats.COVERED, 1.0)
  460. ],
  461. 'emma_2': [
  462. emma_coverage_stats.LineCoverage(
  463. 1, '', emma_coverage_stats.NOT_COVERED, 1.0),
  464. emma_coverage_stats.LineCoverage(
  465. 2, '', emma_coverage_stats.COVERED, 1.0)
  466. ]
  467. }
  468. expected_dict = {
  469. 'files': {
  470. '/path/2/File2.java': {
  471. 'absolute': {'covered': 1, 'total': 2},
  472. 'incremental': {'covered': 1, 'total': 2},
  473. 'source': [{'changed': True, 'coverage': 0,
  474. 'line': '', 'fractional_coverage': 1.0},
  475. {'changed': True, 'coverage': 1,
  476. 'line': '', 'fractional_coverage': 1.0}]
  477. },
  478. '/path/to/1/File1.java': {
  479. 'absolute': {'covered': 2.5, 'total': 3},
  480. 'incremental': {'covered': 2, 'total': 2},
  481. 'source': [{'changed': True, 'coverage': 1,
  482. 'line': '', 'fractional_coverage': 1.0},
  483. {'changed': False, 'coverage': 2,
  484. 'line': '', 'fractional_coverage': 0.5},
  485. {'changed': True, 'coverage': -1,
  486. 'line': '', 'fractional_coverage': 1.0},
  487. {'changed': True, 'coverage': 1,
  488. 'line': '', 'fractional_coverage': 1.0}]
  489. }
  490. },
  491. 'patch': {'incremental': {'covered': 3, 'total': 4}}
  492. }
  493. # Return the relevant coverage info for each file.
  494. self.simple_coverage._emma_parser.GetLineCoverage = (
  495. lambda x: coverage_info[x])
  496. result_dict = self.simple_coverage.GetCoverageDict(files_for_coverage)
  497. self.assertDictEqual(result_dict, expected_dict)
  498. def testGetCoverageDict_noCoverage(self):
  499. result_dict = self.simple_coverage.GetCoverageDict({})
  500. self.assertDictEqual(result_dict, EMPTY_COVERAGE_STATS_DICT)
  501. class EmmaCoverageStatsGenerateCoverageReport(unittest.TestCase):
  502. """Tests for GenerateCoverageReport."""
  503. def testGenerateCoverageReport_missingJsonFile(self):
  504. with self.assertRaises(IOError):
  505. with mock.patch('os.path.exists', return_value=False):
  506. emma_coverage_stats.GenerateCoverageReport('', '', '')
  507. def testGenerateCoverageReport_invalidJsonFile(self):
  508. with self.assertRaises(ValueError):
  509. with mock.patch('os.path.exists', return_value=True):
  510. MockOpenForFunction(emma_coverage_stats.GenerateCoverageReport, [''],
  511. line_coverage_file='', out_file_path='',
  512. coverage_dir='')
  513. def MockOpenForFunction(func, side_effects, **kwargs):
  514. """Allows easy mock open and read for callables that open multiple files.
  515. Will mock the python open function in a way such that each time read() is
  516. called on an open file, the next element in |side_effects| is returned. This
  517. makes it easier to test functions that call open() multiple times.
  518. Args:
  519. func: The callable to invoke once mock files are setup.
  520. side_effects: A list of return values for each file to return once read.
  521. Length of list should be equal to the number calls to open in |func|.
  522. **kwargs: Keyword arguments to be passed to |func|.
  523. Returns:
  524. A tuple containing the return value of |func| and the MagicMock object used
  525. to mock all calls to open respectively.
  526. """
  527. mock_open = mock.mock_open()
  528. mock_open.side_effect = [mock.mock_open(read_data=side_effect).return_value
  529. for side_effect in side_effects]
  530. if sys.version_info.major < 3:
  531. open_builtin_path = '__builtin__.open'
  532. else:
  533. open_builtin_path = 'builtins.open'
  534. with mock.patch(open_builtin_path, mock_open):
  535. return func(**kwargs), mock_open
  536. if __name__ == '__main__':
  537. # Suppress logging messages.
  538. unittest.main(buffer=True)