merge_results_test.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. #!/usr/bin/env vpython3
  2. # Copyright 2019 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. import copy
  6. import json
  7. import os
  8. import subprocess
  9. import sys
  10. import unittest
  11. import mock
  12. import merge_results
  13. import merge_steps
  14. import merge_lib as merger
  15. class MergeProfilesTest(unittest.TestCase):
  16. # pylint: disable=super-with-arguments
  17. def __init__(self, *args, **kwargs):
  18. super(MergeProfilesTest, self).__init__(*args, **kwargs)
  19. self.maxDiff = None
  20. # pylint: enable=super-with-arguments
  21. def test_merge_script_api_parameters(self):
  22. """Test the step-level merge front-end."""
  23. build_properties = json.dumps({
  24. 'some': {
  25. 'complicated': ['nested', {
  26. 'json': None,
  27. 'object': 'thing',
  28. }]
  29. }
  30. })
  31. task_output_dir = 'some/task/output/dir'
  32. profdata_dir = '/some/different/path/to/profdata/default.profdata'
  33. profdata_file = os.path.join(profdata_dir, 'base_unittests.profdata')
  34. args = [
  35. 'script_name', '--output-json', 'output.json', '--build-properties',
  36. build_properties, '--summary-json', 'summary.json', '--task-output-dir',
  37. task_output_dir, '--profdata-dir', profdata_dir, '--llvm-profdata',
  38. 'llvm-profdata', 'a.json', 'b.json', 'c.json', '--test-target-name',
  39. 'base_unittests', '--sparse'
  40. ]
  41. with mock.patch.object(merger, 'merge_profiles') as mock_merge:
  42. mock_merge.return_value = None, None
  43. with mock.patch.object(sys, 'argv', args):
  44. merge_results.main()
  45. self.assertEqual(
  46. mock_merge.call_args,
  47. mock.call(task_output_dir, profdata_file, '.profraw',
  48. 'llvm-profdata', sparse=True,
  49. skip_validation=False), None)
  50. def test_merge_steps_parameters(self):
  51. """Test the build-level merge front-end."""
  52. input_dir = 'some/task/output/dir'
  53. output_file = '/some/different/path/to/profdata/merged.profdata'
  54. args = [
  55. 'script_name',
  56. '--input-dir',
  57. input_dir,
  58. '--output-file',
  59. output_file,
  60. '--llvm-profdata',
  61. 'llvm-profdata',
  62. '--profdata-filename-pattern',
  63. '.*'
  64. ]
  65. with mock.patch.object(merger, 'merge_profiles') as mock_merge:
  66. mock_merge.return_value = None
  67. with mock.patch.object(sys, 'argv', args):
  68. merge_steps.main()
  69. self.assertEqual(
  70. mock_merge.call_args,
  71. mock.call(input_dir, output_file, '.profdata', 'llvm-profdata',
  72. '.*', sparse=False))
  73. @mock.patch.object(merger, '_validate_and_convert_profraws')
  74. def test_merge_profraw(self, mock_validate_and_convert_profraws):
  75. mock_input_dir_walk = [
  76. ('/b/some/path', ['0', '1', '2', '3'], ['summary.json']),
  77. ('/b/some/path/0', [],
  78. ['output.json', 'default-1.profraw', 'default-2.profraw']),
  79. ('/b/some/path/1', [],
  80. ['output.json', 'default-1.profraw', 'default-2.profraw']),
  81. ]
  82. mock_validate_and_convert_profraws.return_value = [
  83. '/b/some/path/0/default-1.profdata',
  84. '/b/some/path/1/default-2.profdata',
  85. ], [
  86. '/b/some/path/0/default-2.profraw',
  87. '/b/some/path/1/default-1.profraw',
  88. ], [
  89. '/b/some/path/1/default-1.profraw',
  90. ]
  91. with mock.patch.object(os, 'walk') as mock_walk:
  92. with mock.patch.object(os, 'remove'):
  93. mock_walk.return_value = mock_input_dir_walk
  94. with mock.patch.object(subprocess, 'check_call') as mock_exec_cmd:
  95. merger.merge_profiles('/b/some/path', 'output/dir/default.profdata',
  96. '.profraw', 'llvm-profdata')
  97. self.assertEqual(
  98. mock.call(
  99. [
  100. 'llvm-profdata',
  101. 'merge',
  102. '-o',
  103. 'output/dir/default.profdata',
  104. '/b/some/path/0/default-1.profdata',
  105. '/b/some/path/1/default-2.profdata',
  106. ],
  107. stderr=-2,
  108. ), mock_exec_cmd.call_args)
  109. self.assertTrue(mock_validate_and_convert_profraws.called)
  110. @mock.patch.object(merger, '_validate_and_convert_profraws')
  111. def test_profraw_skip_validation(self, mock_validate_and_convert_profraws):
  112. mock_input_dir_walk = [
  113. ('/b/some/path', ['0', '1', '2', '3'], ['summary.json']),
  114. ('/b/some/path/0', [],
  115. ['output.json', 'default-1.profraw', 'default-2.profraw']),
  116. ('/b/some/path/1', [],
  117. ['output.json', 'default-1.profraw', 'default-2.profraw']),
  118. ]
  119. with mock.patch.object(os, 'walk') as mock_walk:
  120. with mock.patch.object(os, 'remove'):
  121. mock_walk.return_value = mock_input_dir_walk
  122. with mock.patch.object(subprocess, 'check_call') as mock_exec_cmd:
  123. merger.merge_profiles('/b/some/path',
  124. 'output/dir/default.profdata',
  125. '.profraw',
  126. 'llvm-profdata',
  127. skip_validation=True)
  128. self.assertEqual(
  129. mock.call(
  130. [
  131. 'llvm-profdata',
  132. 'merge',
  133. '-o',
  134. 'output/dir/default.profdata',
  135. '/b/some/path/0/default-1.profraw',
  136. '/b/some/path/0/default-2.profraw',
  137. '/b/some/path/1/default-1.profraw',
  138. '/b/some/path/1/default-2.profraw'
  139. ],
  140. stderr=-2,
  141. ), mock_exec_cmd.call_args)
  142. # Skip validation should've passed all profraw files directly, and
  143. # this validate call should not have been invoked.
  144. self.assertFalse(mock_validate_and_convert_profraws.called)
  145. def test_merge_profraw_skip_if_there_is_no_file(self):
  146. mock_input_dir_walk = [
  147. ('/b/some/path', ['0', '1', '2', '3'], ['summary.json']),
  148. ]
  149. with mock.patch.object(os, 'walk') as mock_walk:
  150. mock_walk.return_value = mock_input_dir_walk
  151. with mock.patch.object(subprocess, 'check_call') as mock_exec_cmd:
  152. merger.merge_profiles('/b/some/path', 'output/dir/default.profdata',
  153. '.profraw', 'llvm-profdata')
  154. self.assertFalse(mock_exec_cmd.called)
  155. @mock.patch.object(merger, '_validate_and_convert_profraws')
  156. def test_merge_profdata(self, mock_validate_and_convert_profraws):
  157. mock_input_dir_walk = [
  158. ('/b/some/path', ['base_unittests', 'url_unittests'], ['summary.json']),
  159. ('/b/some/path/base_unittests', [], ['output.json',
  160. 'default.profdata']),
  161. ('/b/some/path/url_unittests', [], ['output.json', 'default.profdata']),
  162. ]
  163. with mock.patch.object(os, 'walk') as mock_walk:
  164. with mock.patch.object(os, 'remove'):
  165. mock_walk.return_value = mock_input_dir_walk
  166. with mock.patch.object(subprocess, 'check_call') as mock_exec_cmd:
  167. merger.merge_profiles('/b/some/path', 'output/dir/default.profdata',
  168. '.profdata', 'llvm-profdata')
  169. self.assertEqual(
  170. mock.call(
  171. [
  172. 'llvm-profdata',
  173. 'merge',
  174. '-o',
  175. 'output/dir/default.profdata',
  176. '/b/some/path/base_unittests/default.profdata',
  177. '/b/some/path/url_unittests/default.profdata',
  178. ],
  179. stderr=-2,
  180. ), mock_exec_cmd.call_args)
  181. # The mock method should only apply when merging .profraw files.
  182. self.assertFalse(mock_validate_and_convert_profraws.called)
  183. @mock.patch.object(merger, '_validate_and_convert_profraws')
  184. def test_merge_profdata_pattern(self, mock_validate_and_convert_profraws):
  185. mock_input_dir_walk = [
  186. ('/b/some/path', ['base_unittests', 'url_unittests'], ['summary.json']),
  187. ('/b/some/path/base_unittests', [], ['output.json',
  188. 'base_unittests.profdata']),
  189. ('/b/some/path/url_unittests', [], ['output.json',
  190. 'url_unittests.profdata'],),
  191. ('/b/some/path/ios_chrome_smoke_eg2tests',
  192. [], ['output.json','ios_chrome_smoke_eg2tests.profdata'],),
  193. ]
  194. with mock.patch.object(os, 'walk') as mock_walk:
  195. with mock.patch.object(os, 'remove'):
  196. mock_walk.return_value = mock_input_dir_walk
  197. with mock.patch.object(subprocess, 'check_call') as mock_exec_cmd:
  198. input_profdata_filename_pattern = '.+_unittests\.profdata'
  199. merger.merge_profiles('/b/some/path',
  200. 'output/dir/default.profdata',
  201. '.profdata',
  202. 'llvm-profdata',
  203. input_profdata_filename_pattern)
  204. self.assertEqual(
  205. mock.call(
  206. [
  207. 'llvm-profdata',
  208. 'merge',
  209. '-o',
  210. 'output/dir/default.profdata',
  211. '/b/some/path/base_unittests/base_unittests.profdata',
  212. '/b/some/path/url_unittests/url_unittests.profdata',
  213. ],
  214. stderr=-2,
  215. ), mock_exec_cmd.call_args)
  216. # The mock method should only apply when merging .profraw files.
  217. self.assertFalse(mock_validate_and_convert_profraws.called)
  218. @mock.patch('merge_lib._JAVA_PATH', 'java')
  219. def test_merge_java_exec_files(self):
  220. mock_input_dir_walk = [
  221. ('/b/some/path', ['0', '1', '2', '3'], ['summary.json']),
  222. ('/b/some/path/0', [],
  223. ['output.json', 'default-1.exec', 'default-2.exec']),
  224. ('/b/some/path/1', [],
  225. ['output.json', 'default-3.exec', 'default-4.exec']),
  226. ]
  227. with mock.patch.object(os, 'walk') as mock_walk:
  228. mock_walk.return_value = mock_input_dir_walk
  229. with mock.patch.object(subprocess, 'check_call') as mock_exec_cmd:
  230. merger.merge_java_exec_files(
  231. '/b/some/path', 'output/path', 'path/to/jacococli.jar')
  232. self.assertEqual(
  233. mock.call(
  234. [
  235. 'java',
  236. '-jar',
  237. 'path/to/jacococli.jar',
  238. 'merge',
  239. '/b/some/path/0/default-1.exec',
  240. '/b/some/path/0/default-2.exec',
  241. '/b/some/path/1/default-3.exec',
  242. '/b/some/path/1/default-4.exec',
  243. '--destfile',
  244. 'output/path',
  245. ],
  246. stderr=-2,
  247. ), mock_exec_cmd.call_args)
  248. def test_merge_java_exec_files_if_there_is_no_file(self):
  249. mock_input_dir_walk = [
  250. ('/b/some/path', ['0', '1', '2', '3'], ['summary.json']),
  251. ]
  252. with mock.patch.object(os, 'walk') as mock_walk:
  253. mock_walk.return_value = mock_input_dir_walk
  254. with mock.patch.object(subprocess, 'check_call') as mock_exec_cmd:
  255. merger.merge_java_exec_files(
  256. '/b/some/path', 'output/path', 'path/to/jacococli.jar')
  257. self.assertFalse(mock_exec_cmd.called)
  258. def test_calls_merge_js_results_script(self):
  259. task_output_dir = 'some/task/output/dir'
  260. profdata_dir = '/some/different/path/to/profdata/default.profdata'
  261. args = [
  262. 'script_name', '--output-json', 'output.json', '--task-output-dir',
  263. task_output_dir, '--profdata-dir', profdata_dir, '--llvm-profdata',
  264. 'llvm-profdata', 'a.json', 'b.json', 'c.json', '--test-target-name',
  265. 'v8_unittests', '--sparse',
  266. '--javascript-coverage-dir', 'output/dir/devtools_code_coverage',
  267. '--merged-js-cov-filename', 'path/js/cov/filename'
  268. ]
  269. with mock.patch.object(merger, 'merge_profiles') as mock_merge:
  270. mock_merge.return_value = None, None
  271. with mock.patch.object(sys, 'argv', args):
  272. with mock.patch.object(subprocess, 'call') as mock_exec_cmd:
  273. with mock.patch.object(os.path, 'join') as mock_os_path_join:
  274. mock_merge_js_results_path = 'path/to/js/merge_js_results.py'
  275. mock_os_path_join.return_value = mock_merge_js_results_path
  276. python_exec = sys.executable
  277. merge_results.main()
  278. mock_exec_cmd.assert_called_with(
  279. [python_exec, mock_merge_js_results_path, '--task-output-dir',
  280. task_output_dir, '--javascript-coverage-dir',
  281. 'output/dir/devtools_code_coverage',
  282. '--merged-js-cov-filename', 'path/js/cov/filename'])
  283. def test_argparse_sparse(self):
  284. """Ensure that sparse flag defaults to true, and is set to correct value"""
  285. # Basic required args
  286. build_properties = json.dumps({
  287. 'some': {
  288. 'complicated': ['nested', {
  289. 'json': None,
  290. 'object': 'thing',
  291. }]
  292. }
  293. })
  294. task_output_dir = 'some/task/output/dir'
  295. profdata_dir = '/some/different/path/to/profdata/default.profdata'
  296. profdata_file = os.path.join(profdata_dir, 'base_unittests.profdata')
  297. args = [
  298. 'script_name', '--output-json', 'output.json', '--build-properties',
  299. build_properties, '--summary-json', 'summary.json', '--task-output-dir',
  300. task_output_dir, '--profdata-dir', profdata_dir, '--llvm-profdata',
  301. 'llvm-profdata', 'a.json', 'b.json', 'c.json', '--test-target-name',
  302. 'base_unittests'
  303. ]
  304. test_scenarios = [
  305. {
  306. # Base set of args should set --sparse to false by default
  307. 'args': None,
  308. 'expected_outcome': False,
  309. },
  310. {
  311. # Sparse should parse True when only --sparse is specified
  312. 'args': ['--sparse'],
  313. 'expected_outcome': True,
  314. }
  315. ]
  316. for scenario in test_scenarios:
  317. args = copy.deepcopy(args)
  318. additional_args = scenario['args']
  319. if additional_args:
  320. args.extend(additional_args)
  321. expected_outcome = scenario['expected_outcome']
  322. with mock.patch.object(merger, 'merge_profiles') as mock_merge:
  323. mock_merge.return_value = None, None
  324. with mock.patch.object(sys, 'argv', args):
  325. merge_results.main()
  326. self.assertEqual(
  327. mock_merge.call_args,
  328. mock.call(task_output_dir, profdata_file, '.profraw',
  329. 'llvm-profdata', sparse=expected_outcome,
  330. skip_validation=False), None)
  331. if __name__ == '__main__':
  332. unittest.main()