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 ParseTests(unittest.TestCase):
50  def test_parse_definition_1(self):
51    text = """// blahblahblah
52base.require('dependency1');
53base.require('dependency2');
54base.requireStylesheet('myStylesheet');
55"""
56    module = parse_deps.Module("myModule")
57    module.parse_definition_(text)
58    self.assertEquals(["myStylesheet"], module.style_sheet_names);
59    self.assertEquals(["dependency1", "dependency2"],
60                      module.dependent_module_names);
61
62  def test_parse_definition_missing_semis(self):
63    text = """// blahblahblah
64base.require('dependency1')
65base.require('dependency2');
66base.requireStylesheet('myStylesheet')
67"""
68    module = parse_deps.Module("myModule")
69    module.parse_definition_(text)
70    self.assertEquals(["myStylesheet"], module.style_sheet_names);
71    self.assertEquals(["dependency1", "dependency2"],
72                      module.dependent_module_names);
73
74  def test_parse_definition_with_deps_and_stylesheet_swapped(self):
75    text = """// blahblahblah
76base.require('dependency1');
77base.requireStylesheet('myStylesheet');
78base.require('dependency2');
79"""
80    module = parse_deps.Module("myModule")
81    module.parse_definition_(text)
82    self.assertEquals(["myStylesheet"], module.style_sheet_names);
83    self.assertEquals(["dependency1", "dependency2"],
84                      module.dependent_module_names);
85
86  def test_parse_empty_definition(self):
87    text = """// blahblahblah
88"""
89    module = parse_deps.Module("myModule")
90    module.parse_definition_(text, decl_required = False)
91    self.assertEquals([], module.style_sheet_names);
92    self.assertEquals([], module.dependent_module_names);
93
94  def test_parse_definition_3(self):
95    text = """// blahblahblah
96base.require('dependency1');
97//base.require('dependency2');
98"""
99    module = parse_deps.Module("myModule")
100    module.parse_definition_(text)
101    self.assertEquals([], module.style_sheet_names);
102    self.assertEquals(["dependency1"], module.dependent_module_names);
103
104  def test_parse_definition_4(self):
105    text = """// Copyright (c) 2012 The Chromium Authors. All rights reserved.
106// Use of this source code is governed by a BSD-style license that can be
107// found in the LICENSE file.
108
109'use strict';
110
111/**
112 * @fileoverview TimelineView visualizes TRACE_EVENT events using the
113 * tracing.TimelineTrackView component and adds in selection summary and
114 * control buttons.
115 */
116base.requireStylesheet('timeline_view')
117base.require('timeline_track_view');
118base.require('timeline_analysis');
119base.require('overlay');
120base.require('trace_event_importer');
121base.require('linux_perf_importer');
122base.exportsTo('tracing', function() {"""
123
124    module = parse_deps.Module("timeline_view")
125    module.parse_definition_(text)
126    self.assertEquals(["timeline_view"], module.style_sheet_names);
127    self.assertEquals(["timeline_track_view",
128                       "timeline_analysis",
129                       "overlay",
130                       "trace_event_importer",
131                       "linux_perf_importer"], module.dependent_module_names);
132
133  def test_parse_definition_with_definition_in_comments(self):
134    text = """// SomeComment
135/*
136 * All subclasses should depend on linux_perf_parser, e.g.
137 *
138 * base.require('linux_perf_parser');
139 * base.exportTo('tracing', function() { });
140 *
141 */
142base.require('dependency1');
143base.require('dependency2');
144"""
145    module = parse_deps.Module("myModule")
146    module.parse_definition_(text)
147    self.assertEquals([], module.style_sheet_names);
148    self.assertEquals(["dependency1", "dependency2"],
149                      module.dependent_module_names);
150
151  def test_parse_dependency_with_slashes(self):
152    text = """base.require("foo/dependency1")
153"""
154    module = parse_deps.Module("myModule")
155    self.assertRaises(parse_deps.DepsException,
156                      lambda: module.parse_definition_(text))
157
158  def test_parse_dependency_with_dots(self):
159    text = """base.require("foo.dependency1")
160"""
161    module = parse_deps.Module("myModule")
162    module.parse_definition_(text)
163    self.assertEquals([], module.style_sheet_names);
164    self.assertEquals(["foo.dependency1"],
165                      module.dependent_module_names);
166
167
168class ResourceFinderStub(object):
169  def __init__(self):
170    self.modules = {}
171
172  def add_module(self, name, filename, contents):
173    module = {"filename": filename,
174              "contents": contents}
175    self.modules[name] = module
176
177  def find_and_load_module(self, current_module, requested_module_name):
178    if requested_module_name not in self.modules:
179      return None
180    return (self.modules[requested_module_name]["filename"],
181            self.modules[requested_module_name]["contents"])
182
183
184x_contents = """
185base.require('y');
186base.require('z');
187base.exportTo("xyz", function() { });
188"""
189
190y_contents = """
191base.require('z');
192base.exportsTo("xyz", function() { });
193"""
194
195z_contents = """
196base.exportsTo("xyz", function() { });
197"""
198
199class FlattenTests(unittest.TestCase):
200  def test_module(self):
201    resource_finder = ResourceFinderStub()
202    resource_finder.add_module("y", "y.js", y_contents);
203    resource_finder.add_module("z", "z.js", z_contents);
204
205    x_module = parse_deps.Module("x")
206    x_module.load_and_parse("x.js", x_contents)
207
208    all_resources = {}
209    x_module.resolve(all_resources, resource_finder)
210
211    self.assertEquals([all_resources["scripts"]["y"],
212                       all_resources["scripts"]["z"]],
213                      x_module.dependent_modules)
214
215    already_loaded_set = set()
216    load_sequence = []
217    x_module.compute_load_sequence_recursive(load_sequence, already_loaded_set)
218
219    self.assertEquals([all_resources["scripts"]["z"],
220                       all_resources["scripts"]["y"],
221                       x_module],
222                      load_sequence)
223
224
225class ResourceFinderTest(unittest.TestCase):
226  def test_basic(self):
227
228    resource_finder = parse_deps.ResourceFinder(srcdir)
229    module = parse_deps.Module("unittest")
230    module.load_and_parse(os.path.join(srcdir, "unittest.js"))
231    filename, contents = resource_finder.find_and_load_module(module, "base")
232
233    self.assertTrue(os.path.samefile(filename, os.path.join(srcdir, "base.js")))
234    expected_contents = ''
235    with open(os.path.join(srcdir, "base.js")) as f:
236      expected_contents = f.read()
237    self.assertEquals(contents, expected_contents)
238
239  def test_dependency_in_subdir(self):
240    resource_finder = parse_deps.ResourceFinder(srcdir)
241    module = parse_deps.Module("unittest")
242    module.load_and_parse(os.path.join(srcdir, "unittest.js"))
243    filename, contents = resource_finder.find_and_load_module(
244        module, "tracks.track")
245
246    self.assertTrue(os.path.samefile(filename, os.path.join(srcdir, "tracks/track.js")))
247    expected_contents = ''
248    with open(os.path.join(srcdir, "tracks/track.js")) as f:
249      expected_contents = f.read()
250    self.assertEquals(contents, expected_contents)
251
252
253class CalcLoadSequenceTest(unittest.TestCase):
254  def test_one_toplevel_nodeps(self):
255    load_sequence = parse_deps.calc_load_sequence(
256      [os.path.join(srcdir, "unittest.js")], srcdir)
257    name_sequence = [x.name for x in load_sequence]
258    self.assertEquals(["unittest"], name_sequence)
259
260  # Tests that we resolve deps between toplevels.
261  def test_calc_load_sequence_two_toplevels(self):
262    pass
263
264if __name__ == "__main__":
265  unittest.main()
266