• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env vpython3
2# Copyright 2020 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import base64
7import json
8import os
9import shutil
10import tempfile
11import unittest
12
13import merge_js_lib as merger
14from parameterized import parameterized
15
16
17class MergeJSLibTest(unittest.TestCase):
18    def test_write_parsed_scripts(self):
19        test_files = [{
20            'url': '//a/b/c/1.js',
21            'location': ['a', 'b', 'c', '1.js'],
22            'exists': True
23        }, {
24            'url': '//d/e/f/5.js',
25            'location': ['d', 'e', 'f', '5.js'],
26            'exists': True
27        }, {
28            'url': '//a/b/d/7.js',
29            'location': ['a', 'b', 'd', '7.js'],
30            'exists': True
31        }, {
32            'url': 'chrome://test_webui/file.js',
33            'exists': False
34        }, {
35            'url': 'file://testing/file.js',
36            'exists': False
37        }]
38
39        test_script_file = """{
40"text": "test\\ncontents\\n%d",
41"url": "%s",
42"sourceMapURL":"%s"
43}"""
44
45        scripts_dir = None
46        expected_files = []
47
48        try:
49            scripts_dir = tempfile.mkdtemp()
50            for i, test_script in enumerate(test_files):
51                file_path = os.path.join(scripts_dir, '%d.js.json' % i)
52
53                source_map = ""
54                if test_script['exists']:
55                    # Create an inline sourcemap with just the required keys.
56                    source_map_data_url = base64.b64encode(
57                        json.dumps({
58                            "sources":
59                            [os.path.join(*test_script['location'])],
60                            "sourceRoot":
61                            ""
62                        }).encode('utf-8'))
63
64                    source_map = 'data:application/json;base64,' + \
65                        source_map_data_url.decode('utf-8')
66
67                with open(file_path, 'w') as f:
68                    f.write(test_script_file %
69                            (i, test_script['url'], source_map))
70
71                expected_files.append(file_path)
72                if test_script['exists']:
73                    expected_files.append(
74                        os.path.join(scripts_dir, 'parsed_scripts',
75                                     *test_script['location']))
76
77            if len(expected_files) > 0:
78                expected_files.append(
79                    os.path.join(scripts_dir, 'parsed_scripts',
80                                 'parsed_scripts.json'))
81
82            merger.write_parsed_scripts(scripts_dir, source_dir='')
83            actual_files = []
84
85            for root, _, files in os.walk(scripts_dir):
86                for file_name in files:
87                    actual_files.append(os.path.join(root, file_name))
88
89            self.assertCountEqual(expected_files, actual_files)
90        finally:
91            shutil.rmtree(scripts_dir)
92
93    def test_write_parsed_scripts_negative_cases(self):
94        test_files = [{
95            'url': '//a/b/c/1.js',
96            'contents': """{
97"url": "%s"
98}"""
99        }, {
100            'url': '//d/e/f/1.js',
101            'contents': """{
102"text": "test\\ncontents\\n%s"
103}"""
104        }]
105
106        scripts_dir = None
107        expected_files = []
108        try:
109            scripts_dir = tempfile.mkdtemp()
110            for i, test_script in enumerate(test_files):
111                file_path = os.path.join(scripts_dir, '%d.js.json' % i)
112                expected_files.append(file_path)
113                with open(file_path, 'w') as f:
114                    f.write(test_script['contents'] % test_script['url'])
115
116            merger.write_parsed_scripts(scripts_dir)
117
118            actual_files = []
119            for root, _, files in os.walk(scripts_dir):
120                for file_name in files:
121                    actual_files.append(os.path.join(root, file_name))
122
123            self.assertCountEqual(expected_files, actual_files)
124        finally:
125            shutil.rmtree(scripts_dir)
126
127    def test_trailing_curly_brace_stripped(self):
128        test_script_file = """{
129  "text":"test\\ncontents\\n0",
130  "url":"//a/b/c/1.js",
131  "sourceMapURL":"data:application/json;base64,eyJzb3VyY2VzIjogWyJhL2IvYy8xLmpzIl0sICJzb3VyY2VSb290IjogIiJ9"
132}}"""
133
134        scripts_dir = None
135
136        try:
137            scripts_dir = tempfile.mkdtemp()
138            file_path = os.path.join(scripts_dir, '0.js.json')
139            with open(file_path, 'w') as f:
140                f.write(test_script_file)
141            expected_files = [
142                file_path,
143                os.path.join(scripts_dir, 'parsed_scripts', 'a', 'b', 'c',
144                             '1.js'),
145                os.path.join(scripts_dir, 'parsed_scripts',
146                             'parsed_scripts.json')
147            ]
148
149            merger.write_parsed_scripts(scripts_dir, source_dir='')
150            actual_files = []
151
152            for root, _, files in os.walk(scripts_dir):
153                for file_name in files:
154                    actual_files.append(os.path.join(root, file_name))
155
156            self.assertCountEqual(expected_files, actual_files)
157        finally:
158            shutil.rmtree(scripts_dir)
159
160    def test_non_data_urls_are_ignored(self):
161        test_script_file = """{
162"text": "test\\ncontents",
163"url": "http://test_url",
164"sourceMapURL":"%s"
165}"""
166
167        scripts_dir = None
168        expected_files = []
169
170        try:
171            scripts_dir = tempfile.mkdtemp()
172            file_path = os.path.join(scripts_dir, 'external_map.js.json')
173            expected_files = [file_path]
174
175            # Write a script with an external URL as the sourcemap, this should
176            # exclude it from being written to disk.
177            with open(file_path, 'w') as f:
178                f.write(test_script_file % 'external.map')
179
180            merger.write_parsed_scripts(scripts_dir, source_dir='')
181            actual_files = []
182
183            for root, _, files in os.walk(scripts_dir):
184                for file_name in files:
185                    actual_files.append(os.path.join(root, file_name))
186
187            self.assertCountEqual(expected_files, actual_files)
188        finally:
189            shutil.rmtree(scripts_dir)
190
191    def test_uninteresting_lines_are_excluded(self):
192        """This contrived istanbul coverage file represents the coverage from
193        the following example file:
194        """
195        example_test_file = """// Copyright 2019 The Chromium Authors
196// Use of this source code is governed by a BSD-style license that can be
197// found in the LICENSE file.
198
199import './iframe.js';
200
201/*
202 * function comment should be excluded.
203 */
204export const add = (a, b) => a + b; // should not be excluded
205
206/* should be excluded */
207
208"""
209
210        test_istanbul_file = """{
211"%s":{
212  "path":"%s",
213  "all":false,
214  "statementMap":{
215    "1":{"start":{"line":1,"column":0},"end":{"line":1,"column":38}},
216    "2":{"start":{"line":2,"column":0},"end":{"line":2,"column":73}},
217    "3":{"start":{"line":3,"column":0},"end":{"line":3,"column":29}},
218    "4":{"start":{"line":4,"column":0},"end":{"line":4,"column":0}},
219    "5":{"start":{"line":5,"column":0},"end":{"line":5,"column":21}},
220    "6":{"start":{"line":6,"column":0},"end":{"line":6,"column":0}},
221    "7":{"start":{"line":7,"column":0},"end":{"line":7,"column":2}},
222    "8":{"start":{"line":8,"column":0},"end":{"line":8,"column":39}},
223    "9":{"start":{"line":9,"column":0},"end":{"line":9,"column":3}},
224    "10":{"start":{"line":10,"column":0},"end":{"line":10,"column":61}},
225    "11":{"start":{"line":11,"column":0},"end":{"line":11,"column":0}},
226    "12":{"start":{"line":12,"column":0},"end":{"line":12,"column":24}},
227    "13":{"start":{"line":13,"column":0},"end":{"line":13,"column":0}}
228  },
229  "s":{
230    "1": 1,
231    "2": 1,
232    "3": 1,
233    "4": 1,
234    "5": 1,
235    "6": 1,
236    "7": 1,
237    "8": 1,
238    "9": 1,
239    "10": 1,
240    "11": 1,
241    "12": 1,
242    "13": 1
243  }
244}
245        }"""
246
247        expected_output_file = """{
248            "%s": {
249                "path": "%s",
250                "all": false,
251                "statementMap": {
252                    "10": {
253                        "start": {
254                            "line": 10,
255                            "column": 0
256                        },
257                        "end": {
258                            "line": 10,
259                            "column": 61
260                        }
261                    }
262                },
263                "s": {
264                    "10": 1
265                }
266            }
267        }"""
268
269        try:
270            test_dir = tempfile.mkdtemp()
271            file_path = os.path.join(test_dir,
272                                     'coverage.json').replace('\\', '/')
273            example_test_file_path = os.path.join(test_dir,
274                                                  'fileA.js').replace(
275                                                      '\\', '/')
276            expected_output = json.loads(
277                expected_output_file %
278                (example_test_file_path, example_test_file_path))
279
280            # Set up the tests files so that exclusions can be performed.
281            with open(file_path, 'w') as f:
282                f.write(test_istanbul_file %
283                        (example_test_file_path, example_test_file_path))
284            with open(example_test_file_path, 'w') as f:
285                f.write(example_test_file)
286
287            # Perform the exclusion.
288            merger.exclude_uninteresting_lines(file_path)
289
290            # Assert the final `coverage.json` file matches the expected output.
291            with open(file_path, 'rb') as f:
292                coverage_json = json.load(f)
293                self.assertEqual(coverage_json, expected_output)
294
295        finally:
296            shutil.rmtree(test_dir)
297
298    def test_paths_are_remapped_and_removed(self):
299        test_file_data = """{
300          "/path/to/checkout/chrome/browser/fileA.js": {
301            "path": "/path/to/checkout/chrome/browser/fileA.js"
302          },
303          "/path/to/checkout/out/dir/chrome/browser/fileB.js": {
304            "path": "/path/to/checkout/out/dir/chrome/browser/fileB.js"
305          },
306          "/some/random/path/fileC.js": {
307            "path": "/some/random/path/fileC.js"
308          }
309        }"""
310
311        expected_after_remap = {
312            "chrome/browser/fileA.js": {
313                "path": "chrome/browser/fileA.js"
314            }
315        }
316
317        try:
318            test_dir = tempfile.mkdtemp()
319            coverage_file_path = os.path.join(test_dir,
320                                              'coverage.json').replace(
321                                                  '\\', '/')
322
323            with open(coverage_file_path, 'w', encoding='utf-8',
324                      newline='') as f:
325                f.write(test_file_data)
326
327            merger.remap_paths_to_relative(coverage_file_path,
328                                           "/path/to/checkout",
329                                           "/path/to/checkout/out/dir")
330
331            with open(coverage_file_path, 'rb') as f:
332                coverage_json = json.load(f)
333                self.assertEqual(coverage_json, expected_after_remap)
334
335        finally:
336            shutil.rmtree(test_dir)
337
338    @parameterized.expand([
339        ('// test', True),
340        ('/* test', True),
341        ('*/ test', True),
342        (' * test', True),
343        ('import test', True),
344        (' x = 5 /* comment */', False),
345        ('x = 5', False),
346    ])
347    def test_should_exclude(self, line, exclude):
348        self.assertEqual(merger.should_exclude(line), exclude)
349
350
351if __name__ == '__main__':
352    unittest.main()
353