1#!/usr/bin/env python
2# Copyright (c) 2012 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.
5import unittest
6
7import parse_deps
8import os
9
10srcdir = os.path.join(os.path.dirname(__file__), '../src')
11
12class JSStripTests(unittest.TestCase):
13  def test_tokenize_0(self):
14    tokens = list(parse_deps._tokenize_js(''))
15    self.assertEquals([], tokens)
16
17  def test_tokenize_nl(self):
18    tokens = list(parse_deps._tokenize_js('\n'))
19    self.assertEquals(['\n'], tokens)
20
21  def test_tokenize_slashslash_comment(self):
22    tokens = list(parse_deps._tokenize_js('A // foo'))
23    self.assertEquals(['A ', '//', ' foo'], tokens)
24
25  def test_tokenize_slashslash_comment_then_newline2(self):
26    tokens = list(parse_deps._tokenize_js("""A // foo
27bar"""
28))
29    self.assertEquals(['A ', '//', ' foo', '\n', 'bar'], tokens)
30
31  def test_tokenize_cstyle_comment(self):
32    tokens = list(parse_deps._tokenize_js("""A /* foo */"""))
33    self.assertEquals(['A ', '/*', ' foo ', '*/'], tokens)
34
35  def test_tokenize_cstyle_comment(self):
36    tokens = list(parse_deps._tokenize_js("""A /* foo
37*bar
38*/"""))
39    self.assertEquals(['A ', '/*', ' foo', '\n', '*bar', '\n', '*/'], tokens)
40
41  def test_strip_comments(self):
42    self.assertEquals('A ', parse_deps._strip_js_comments('A // foo'))
43
44    self.assertEquals('A  b', parse_deps._strip_js_comments('A /* foo */ b'))
45    self.assertEquals('A  b', parse_deps._strip_js_comments("""A /* foo
46 */ b"""))
47
48
49class ValidateTests(unittest.TestCase):
50  def test_validate_1(self):
51    text = """// blahblahblah
52
53'use strict';
54
55base.require('dependency1');
56"""
57    module = parse_deps.Module('myModule')
58    stripped_text = parse_deps._strip_js_comments(text)
59    module.validate_uses_strict_mode_(stripped_text)
60
61  def test_validate_2(self):
62    text = """// blahblahblah
63
64base.require('dependency1');
65"""
66    module = parse_deps.Module('myModule')
67    stripped_text = parse_deps._strip_js_comments(text)
68    self.assertRaises(lambda: module.validate_uses_strict_mode_(stripped_text))
69
70class ParseTests(unittest.TestCase):
71  def test_parse_definition_1(self):
72    text = """// blahblahblah
73'use strict';
74base.require('dependency1');
75base.require('dependency2');
76base.requireStylesheet('myStylesheet');
77"""
78    module = parse_deps.Module('myModule')
79    stripped_text = parse_deps._strip_js_comments(text)
80    module.parse_definition_(stripped_text)
81    self.assertEquals(['myStylesheet'], module.style_sheet_names);
82    self.assertEquals(['dependency1', 'dependency2'],
83                      module.dependent_module_names);
84
85  def test_parse_definition_missing_semis(self):
86    text = """// blahblahblah
87'use strict';
88base.require('dependency1')
89base.require('dependency2');
90base.requireStylesheet('myStylesheet')
91"""
92    module = parse_deps.Module('myModule')
93    stripped_text = parse_deps._strip_js_comments(text)
94    module.parse_definition_(stripped_text)
95    self.assertEquals(['myStylesheet'], module.style_sheet_names);
96    self.assertEquals(['dependency1', 'dependency2'],
97                      module.dependent_module_names);
98
99  def test_parse_definition_with_deps_and_stylesheet_swapped(self):
100    text = """// blahblahblah
101'use strict';
102base.require('dependency1');
103base.requireStylesheet('myStylesheet');
104base.require('dependency2');
105"""
106    module = parse_deps.Module('myModule')
107    stripped_text = parse_deps._strip_js_comments(text)
108    module.parse_definition_(stripped_text)
109    self.assertEquals(['myStylesheet'], module.style_sheet_names);
110    self.assertEquals(['dependency1', 'dependency2'],
111                      module.dependent_module_names);
112
113  def test_parse_empty_definition(self):
114    text = """// blahblahblah
115'use strict';
116"""
117    module = parse_deps.Module('myModule')
118    stripped_text = parse_deps._strip_js_comments(text)
119    module.parse_definition_(stripped_text, decl_required=False)
120    self.assertEquals([], module.style_sheet_names);
121    self.assertEquals([], module.dependent_module_names);
122
123  def test_parse_definition_3(self):
124    text = """// blahblahblah
125'use strict';
126base.require('dependency1');
127//base.require('dependency2');
128"""
129    module = parse_deps.Module('myModule')
130    stripped_text = parse_deps._strip_js_comments(text)
131    module.parse_definition_(stripped_text)
132    self.assertEquals([], module.style_sheet_names);
133    self.assertEquals(['dependency1'], module.dependent_module_names);
134
135  def test_parse_definition_4(self):
136    text = """// Copyright (c) 2012 The Chromium Authors. All rights reserved.
137// Use of this source code is governed by a BSD-style license that can be
138// found in the LICENSE file.
139
140'use strict';
141
142/**
143 * @fileoverview TimelineView visualizes TRACE_EVENT events using the
144 * tracing.TimelineTrackView component and adds in selection summary and
145 * control buttons.
146 */
147base.requireStylesheet('timeline_view')
148base.require('timeline_track_view');
149base.require('timeline_analysis');
150base.require('overlay');
151base.require('trace_event_importer');
152base.require('linux_perf_importer');
153base.exportsTo('tracing', function() {"""
154
155    module = parse_deps.Module('timeline_view')
156    stripped_text = parse_deps._strip_js_comments(text)
157    module.parse_definition_(stripped_text)
158    self.assertEquals(['timeline_view'], module.style_sheet_names);
159    self.assertEquals(['timeline_track_view',
160                       'timeline_analysis',
161                       'overlay',
162                       'trace_event_importer',
163                       'linux_perf_importer'], module.dependent_module_names);
164
165  def test_parse_definition_with_definition_in_comments(self):
166    text = """// SomeComment
167/*
168 * All subclasses should depend on linux_perf_parser, e.g.
169 *
170 * base.require('linux_perf_parser');
171 * base.exportTo('tracing', function() { });
172 *
173 */
174'use strict';
175base.require('dependency1');
176base.require('dependency2');
177"""
178    module = parse_deps.Module('myModule')
179    stripped_text = parse_deps._strip_js_comments(text)
180    module.parse_definition_(stripped_text)
181    self.assertEquals([], module.style_sheet_names);
182    self.assertEquals(['dependency1', 'dependency2'],
183                      module.dependent_module_names);
184
185  def test_parse_dependency_with_slashes(self):
186    text = """base.require('foo/dependency1')
187"""
188    module = parse_deps.Module('myModule')
189    self.assertRaises(parse_deps.DepsException,
190                      lambda: module.parse_definition_(text))
191
192  def test_parse_dependency_with_dots(self):
193    text = """base.require('foo.dependency1')
194"""
195    module = parse_deps.Module('myModule')
196    stripped_text = parse_deps._strip_js_comments(text)
197    module.parse_definition_(stripped_text)
198    self.assertEquals([], module.style_sheet_names);
199    self.assertEquals(['foo.dependency1'],
200                      module.dependent_module_names);
201
202
203class ResourceFinderStub(object):
204  def __init__(self):
205    self.modules = {}
206
207  def add_module(self, name, filename, contents):
208    module = {'filename': filename,
209              'contents': contents}
210    self.modules[name] = module
211
212  def find_and_load_module(self, current_module, requested_module_name):
213    if requested_module_name not in self.modules:
214      return None
215    return (self.modules[requested_module_name]['filename'],
216            self.modules[requested_module_name]['contents'])
217
218
219x_contents = """
220'use strict';
221base.require('y');
222base.require('z');
223base.exportTo('xyz', function() { });
224"""
225
226y_contents = """
227'use strict';
228base.require('z');
229base.exportsTo('xyz', function() { });
230"""
231
232z_contents = """
233'use strict';
234base.exportsTo('xyz', function() { });
235"""
236
237class FlattenTests(unittest.TestCase):
238  def test_module(self):
239    resource_finder = ResourceFinderStub()
240    resource_finder.add_module('y', 'y.js', y_contents);
241    resource_finder.add_module('z', 'z.js', z_contents);
242
243    x_module = parse_deps.Module('x')
244    x_module.load_and_parse('x.js', x_contents)
245
246    all_resources = {}
247    x_module.resolve(all_resources, resource_finder)
248
249    self.assertEquals([all_resources['scripts']['y'],
250                       all_resources['scripts']['z']],
251                      x_module.dependent_modules)
252
253    already_loaded_set = set()
254    load_sequence = []
255    x_module.compute_load_sequence_recursive(load_sequence, already_loaded_set)
256
257    self.assertEquals([all_resources['scripts']['z'],
258                       all_resources['scripts']['y'],
259                       x_module],
260                      load_sequence)
261
262
263class ResourceFinderTest(unittest.TestCase):
264  def test_basic(self):
265
266    resource_finder = parse_deps.ResourceFinder(srcdir)
267    module = parse_deps.Module('guid')
268    module.load_and_parse(os.path.join(srcdir, 'base', 'guid.js'))
269    filename, contents = resource_finder.find_and_load_module(module, 'base')
270
271    self.assertTrue(os.path.samefile(filename, os.path.join(srcdir, 'base.js')))
272    expected_contents = ''
273    with open(os.path.join(srcdir, 'base.js')) as f:
274      expected_contents = f.read()
275    self.assertEquals(contents, expected_contents)
276
277  def test_dependency_in_subdir(self):
278    resource_finder = parse_deps.ResourceFinder(srcdir)
279    module = parse_deps.Module('base.guid')
280    module.load_and_parse(os.path.join(srcdir, 'base', 'guid.js'))
281    filename, contents = resource_finder.find_and_load_module(
282        module, 'tracing.tracks.track')
283
284    assert filename
285
286    self.assertTrue(os.path.samefile(filename, os.path.join(
287      srcdir, 'tracing', 'tracks', 'track.js')))
288    expected_contents = ''
289    with open(os.path.join(srcdir, 'tracing', 'tracks', 'track.js')) as f:
290      expected_contents = f.read()
291    self.assertEquals(contents, expected_contents)
292
293
294class CalcLoadSequenceTest(unittest.TestCase):
295  def test_one_toplevel_nodeps(self):
296    load_sequence = parse_deps.calc_load_sequence(
297      [os.path.join(srcdir, 'base', 'guid.js')], srcdir)
298    name_sequence = [x.name for x in load_sequence]
299    self.assertEquals(['base.guid'], name_sequence)
300
301  # Tests that we resolve deps between toplevels.
302  def test_calc_load_sequence_two_toplevels(self):
303    pass
304
305if __name__ == '__main__':
306  unittest.main()
307