| #!/usr/bin/env vpython3 |
| # Copyright 2021 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import json |
| import os |
| from pathlib import Path |
| import shutil |
| import tempfile |
| import unittest |
| |
| import merge_js_lib as merger |
| import node |
| |
| _HERE_DIR = Path(__file__).parent.resolve() |
| _SOURCE_MAP_PROCESSOR = (_HERE_DIR.parent.parent.parent / 'tools' / |
| 'code_coverage' / 'js_source_maps' / |
| 'create_js_source_maps' / |
| 'create_js_source_maps.js').resolve() |
| |
| |
| @unittest.skipIf(os.name == 'nt', 'Not intended to work on Windows') |
| class ConvertToIstanbulTest(unittest.TestCase): |
| _TEST_SOURCE_A = """function add(a, b) { |
| return a + b; |
| } |
| |
| function subtract(a, b) { |
| return a - b; |
| } |
| |
| subtract(5, 2); |
| """ |
| _INVALID_MAPPING_A = ( |
| '//# sourceMappingURL=data:application/json;base64,' |
| 'eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZvby50cyJdLCJuYW1lcyI6W10sIm1hcHBpb' |
| 'mdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Oz' |
| 's7OztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN' |
| 'BIiwiZmlsZSI6Ii91c3IvbG9jYWwvZ29vZ2xlL2hvbWUvc3Jpbml2YXNoZWdkZS9jaHJv' |
| 'bWl1bS9zcmMvZm9vX3ByZS50cyIsInNvdXJjZVJvb3QiOiIvdXNyL2xvY2FsL2dvb2dsZ' |
| 'S9ob21lL3NyaW5pdmFzaGVnZGUvY2hyb21pdW0vc3JjIiwic291cmNlc0NvbnRlbnQiOl' |
| 'siZnVuY3Rpb24gYWRkKGEsIGIpIHtcbiAgcmV0dXJuIGEgKyBiO1xufVxuXG5mdW5jdGl' |
| 'vbiBzdWJ0cmFjdChhLCBiKSB7XG4gIHJldHVybiBhIC0gYjtcbn1cblxuc3VidHJhY3Qo' |
| 'NSwgMik7XG4iXX0=') |
| |
| _TEST_COVERAGE_A = """{ |
| "result": [ |
| { |
| "scriptId":"72", |
| "url":"//file.js", |
| "functions":[ |
| { |
| "functionName":"", |
| "ranges":[ |
| {"startOffset":0,"endOffset":101,"count":1} |
| ], |
| "isBlockCoverage":true |
| }, |
| { |
| "functionName":"add", |
| "ranges":[ |
| {"startOffset":0,"endOffset":38,"count":0} |
| ], |
| "isBlockCoverage":false |
| }, |
| { |
| "functionName":"subtract", |
| "ranges":[ |
| {"startOffset":40,"endOffset":83,"count":1} |
| ], |
| "isBlockCoverage":true |
| } |
| ] |
| } |
| ] |
| } |
| """ |
| |
| _TEST_COVERAGE_INVALID = """{ |
| "scriptId":"72", |
| "url":"//file.js", |
| "functions":[ |
| { |
| "functionName":"", |
| "ranges":[ |
| {"startOffset":0,"endOffset":101,"count":1} |
| ], |
| "isBlockCoverage":true |
| }, |
| { |
| "functionName":"add", |
| "ranges":[ |
| {"startOffset":0,"endOffset":38,"count":0} |
| ], |
| "isBlockCoverage":false |
| }, |
| { |
| "functionName":"subtract", |
| "ranges":[ |
| {"startOffset":40,"endOffset":83,"count":1} |
| ], |
| "isBlockCoverage":true |
| } |
| ] |
| } |
| """ |
| |
| _TEST_SOURCE_B = """const {subtract} = require('./test1.js'); |
| |
| function add(a, b) { |
| return a + b; |
| } |
| |
| subtract(5, 2); |
| |
| """ |
| |
| _TEST_SOURCE_C = """exports.subtract = function(a, b) { |
| return a - b; |
| } |
| """ |
| |
| _TEST_COVERAGE_B = """{ |
| "result":[ |
| { |
| "scriptId":"72", |
| "url":"//test.js", |
| "functions":[ |
| { |
| "functionName":"", |
| "ranges":[ |
| {"startOffset":0,"endOffset":99,"count":1} |
| ], |
| "isBlockCoverage":true |
| }, |
| { |
| "functionName":"add", |
| "ranges":[ |
| {"startOffset":43,"endOffset":81,"count":0} |
| ], |
| "isBlockCoverage":false |
| } |
| ] |
| }, |
| { |
| "scriptId":"73", |
| "url":"//test1.js", |
| "functions":[ |
| { |
| "functionName":"", |
| "ranges":[ |
| {"startOffset":0,"endOffset":54,"count":1} |
| ], |
| "isBlockCoverage":true |
| }, |
| { |
| "functionName":"exports.subtract", |
| "ranges":[ |
| {"startOffset":19,"endOffset":53,"count":1} |
| ], |
| "isBlockCoverage":true |
| } |
| ] |
| } |
| ] |
| } |
| """ |
| |
| _TEST_COVERAGE_NO_LEADING_SLASH = """{ |
| "result":[ |
| { |
| "scriptId":"72", |
| "url":"file:///usr/local/google/home/benreich/v8-to-istanbul/test.js", |
| "functions":[ |
| { |
| "functionName":"", |
| "ranges":[ |
| {"startOffset":0,"endOffset":99,"count":1} |
| ], |
| "isBlockCoverage":true |
| }, |
| { |
| "functionName":"add", |
| "ranges":[ |
| {"startOffset":43,"endOffset":81,"count":0} |
| ], |
| "isBlockCoverage":false |
| } |
| ] |
| }, |
| { |
| "scriptId":"73", |
| "url":"//test1.js", |
| "functions":[ |
| { |
| "functionName":"", |
| "ranges":[ |
| {"startOffset":0,"endOffset":54,"count":1} |
| ], |
| "isBlockCoverage":true |
| }, |
| { |
| "functionName":"exports.subtract", |
| "ranges":[ |
| {"startOffset":19,"endOffset":53,"count":1} |
| ], |
| "isBlockCoverage":true |
| } |
| ] |
| } |
| ] |
| } |
| """ |
| |
| _TEST_COVERAGE_DUPLICATE_SINGLE = """{ |
| "result":[ |
| { |
| "scriptId":"73", |
| "url":"//test1.js", |
| "functions":[ |
| { |
| "functionName":"", |
| "ranges":[ |
| {"startOffset":0,"endOffset":54,"count":1} |
| ], |
| "isBlockCoverage":true |
| }, |
| { |
| "functionName":"exports.subtract", |
| "ranges":[ |
| {"startOffset":19,"endOffset":53,"count":1} |
| ], |
| "isBlockCoverage":true |
| } |
| ] |
| } |
| ] |
| } |
| """ |
| |
| _TEST_COVERAGE_DUPLICATE_DOUBLE = """{ |
| "result":[ |
| { |
| "scriptId":"72", |
| "url":"//test.js", |
| "functions":[ |
| { |
| "functionName":"", |
| "ranges":[ |
| {"startOffset":0,"endOffset":99,"count":1} |
| ], |
| "isBlockCoverage":true |
| }, |
| { |
| "functionName":"add", |
| "ranges":[ |
| {"startOffset":43,"endOffset":81,"count":0} |
| ], |
| "isBlockCoverage":false |
| } |
| ] |
| }, |
| { |
| "scriptId":"73", |
| "url":"//test1.js", |
| "functions":[ |
| { |
| "functionName":"", |
| "ranges":[ |
| {"startOffset":0,"endOffset":54,"count":1} |
| ], |
| "isBlockCoverage":true |
| }, |
| { |
| "functionName":"exports.subtract", |
| "ranges":[ |
| {"startOffset":19,"endOffset":53,"count":1} |
| ], |
| "isBlockCoverage":true |
| } |
| ] |
| } |
| ] |
| } |
| """ |
| |
| def setUp(self): |
| self.task_output_dir = tempfile.mkdtemp() |
| self.coverage_dir = os.path.join(self.task_output_dir, 'coverages') |
| self.source_dir = os.path.join(self.task_output_dir, 'source') |
| self.out_dir = os.path.join(self.task_output_dir, 'out') |
| self.sourceRoot = '/' |
| |
| os.makedirs(self.coverage_dir) |
| os.makedirs(self.source_dir) |
| os.makedirs(self.out_dir) |
| |
| def tearDown(self): |
| shutil.rmtree(self.task_output_dir) |
| |
| def list_files(self, absolute_path): |
| actual_files = [] |
| for root, _, files in os.walk(absolute_path): |
| actual_files.extend( |
| [os.path.join(root, file_name) for file_name in files]) |
| |
| return actual_files |
| |
| def _write_files(self, root_dir, *file_path_contents): |
| for data in file_path_contents: |
| file_path, contents = data |
| with open(os.path.join(root_dir, file_path), 'w') as f: |
| f.write(contents) |
| |
| def _write_transformations(self, source_dir, out_dir, original_file_name, |
| input_file_name, output_file_name): |
| original_file = os.path.join(source_dir, original_file_name) |
| input_file = os.path.join(source_dir, input_file_name) |
| output_file = os.path.join(out_dir, output_file_name) |
| node.RunNode([ |
| str(_SOURCE_MAP_PROCESSOR), |
| '--originals={}'.format(' '.join([original_file])), |
| '--inputs={}'.format(' '.join([input_file])), |
| '--outputs={}'.format(' '.join([output_file])), |
| '--inline-sourcemaps', |
| '--sourceRoot={}'.format(self.sourceRoot), |
| ]) |
| |
| def write_sources(self, *file_path_contents): |
| url_to_path_map = {} |
| for path_url, contents in file_path_contents: |
| file_path, url = path_url |
| url_to_path_map[file_path] = url |
| self._write_files(self.source_dir, (url, contents)) |
| self._write_files(self.out_dir, (url, contents)) |
| self._write_transformations(self.source_dir, self.out_dir, url, url, url) |
| with open(os.path.join(self.out_dir, 'parsed_scripts.json'), |
| 'w', |
| encoding='utf-8') as f: |
| f.write(json.dumps(url_to_path_map)) |
| |
| def write_coverages(self, *file_path_contents): |
| self._write_files(self.coverage_dir, *file_path_contents) |
| |
| def test_happy_path(self): |
| self.write_sources((('//file.js', 'file.js'), self._TEST_SOURCE_A)) |
| self.write_coverages(('test_coverage.cov.json', self._TEST_COVERAGE_A)) |
| |
| merger.convert_raw_coverage_to_istanbul([self.coverage_dir], self.out_dir, |
| self.task_output_dir) |
| |
| istanbul_files = self.list_files( |
| os.path.join(self.task_output_dir, 'istanbul')) |
| self.assertEqual(len(istanbul_files), 1) |
| |
| def test_invalid_mapping(self): |
| self.write_sources((('//file.js', 'file.js'), self._TEST_SOURCE_A)) |
| self._write_files( |
| self.out_dir, |
| ('file.js', self._TEST_SOURCE_A + '\n' + self._INVALID_MAPPING_A)) |
| self.write_coverages(('test_coverage.cov.json', self._TEST_COVERAGE_A)) |
| |
| merger.convert_raw_coverage_to_istanbul([self.coverage_dir], self.out_dir, |
| self.task_output_dir) |
| |
| istanbul_files = self.list_files( |
| os.path.join(self.task_output_dir, 'istanbul')) |
| self.assertEqual(len(istanbul_files), 0) |
| |
| def test_no_coverages_in_file(self): |
| coverage_file = """{ |
| "result": [] |
| } |
| """ |
| |
| self.write_sources((('//file.js', 'file.js'), self._TEST_SOURCE_A)) |
| self.write_coverages(('test_coverage.cov.json', coverage_file)) |
| |
| merger.convert_raw_coverage_to_istanbul([self.coverage_dir], self.out_dir, |
| self.task_output_dir) |
| |
| istanbul_files = self.list_files( |
| os.path.join(self.task_output_dir, 'istanbul')) |
| self.assertEqual(len(istanbul_files), 0) |
| |
| def test_invalid_coverage_file(self): |
| self.write_sources((('//file.js', 'file.js'), self._TEST_SOURCE_A)) |
| self.write_coverages( |
| ('test_coverage.cov.json', self._TEST_COVERAGE_INVALID)) |
| |
| with self.assertRaises(RuntimeError): |
| merger.convert_raw_coverage_to_istanbul([self.coverage_dir], self.out_dir, |
| self.task_output_dir) |
| |
| def test_multiple_coverages_single_file(self): |
| self.write_sources((('//test.js', 'test.js'), self._TEST_SOURCE_B), |
| (('//test1.js', 'test1.js'), self._TEST_SOURCE_C)) |
| self.write_coverages(('test_coverage.cov.json', self._TEST_COVERAGE_B)) |
| |
| merger.convert_raw_coverage_to_istanbul([self.coverage_dir], self.out_dir, |
| self.task_output_dir) |
| |
| istanbul_files = self.list_files( |
| os.path.join(self.task_output_dir, 'istanbul')) |
| self.assertEqual(len(istanbul_files), 2) |
| |
| def test_multiple_coverages_no_leading_double_slash(self): |
| self.write_sources((('//test.js', 'test.js'), self._TEST_SOURCE_B), |
| (('//test1.js', 'test1.js'), self._TEST_SOURCE_C)) |
| self.write_coverages( |
| ('test_coverage.cov.json', self._TEST_COVERAGE_NO_LEADING_SLASH)) |
| |
| merger.convert_raw_coverage_to_istanbul([self.coverage_dir], self.out_dir, |
| self.task_output_dir) |
| |
| istanbul_files = self.list_files( |
| os.path.join(self.task_output_dir, 'istanbul')) |
| self.assertEqual(len(istanbul_files), 1) |
| |
| def test_multiple_duplicate_coverages_flattened(self): |
| self.write_sources((('//test.js', 'test.js'), self._TEST_SOURCE_B), |
| (('//test1.js', 'test1.js'), self._TEST_SOURCE_C)) |
| self.write_coverages(('test_coverage_1.cov.json', self._TEST_COVERAGE_B)) |
| self.write_coverages( |
| ('test_coverage_2.cov.json', self._TEST_COVERAGE_DUPLICATE_DOUBLE)) |
| |
| merger.convert_raw_coverage_to_istanbul([self.coverage_dir], self.out_dir, |
| self.task_output_dir) |
| |
| istanbul_files = self.list_files( |
| os.path.join(self.task_output_dir, 'istanbul')) |
| self.assertEqual(len(istanbul_files), 2) |
| |
| def test_original_source_missing(self): |
| self.write_sources((('//file.js', 'file.js'), self._TEST_SOURCE_A)) |
| self.write_coverages(('test_coverage.cov.json', self._TEST_COVERAGE_A)) |
| os.remove(os.path.join(self.source_dir, 'file.js')) |
| |
| merger.convert_raw_coverage_to_istanbul([self.coverage_dir], self.out_dir, |
| self.task_output_dir) |
| |
| istanbul_files = self.list_files( |
| os.path.join(self.task_output_dir, 'istanbul')) |
| self.assertEqual(len(istanbul_files), 0) |
| |
| def test_multiple_coverages_in_multiple_shards(self): |
| coverage_dir_1 = os.path.join(self.coverage_dir, 'coverage1') |
| coverage_dir_2 = os.path.join(self.coverage_dir, 'coverage2') |
| os.makedirs(coverage_dir_1) |
| os.makedirs(coverage_dir_2) |
| |
| self.write_sources((('//test.js', 'test.js'), self._TEST_SOURCE_B), |
| (('//test1.js', 'test1.js'), self._TEST_SOURCE_C)) |
| self._write_files(coverage_dir_1, |
| ('test_coverage_1.cov.json', self._TEST_COVERAGE_B)) |
| self._write_files( |
| coverage_dir_2, |
| ('test_coverage_2.cov.json', self._TEST_COVERAGE_DUPLICATE_DOUBLE)) |
| |
| merger.convert_raw_coverage_to_istanbul([coverage_dir_1, coverage_dir_2], |
| self.out_dir, self.task_output_dir) |
| |
| istanbul_files = self.list_files( |
| os.path.join(self.task_output_dir, 'istanbul')) |
| self.assertEqual(len(istanbul_files), 2) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |