3 # Copyright 2010 The Closure Linter Authors. All Rights Reserved.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS-IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Unit tests for ClosurizedNamespacesInfo."""
21 import unittest as googletest
22 from closure_linter import closurizednamespacesinfo
23 from closure_linter import javascriptstatetracker
24 from closure_linter import javascripttokenizer
25 from closure_linter import javascripttokens
26 from closure_linter import tokenutil
28 # pylint: disable-msg=C6409
29 TokenType = javascripttokens.JavaScriptTokenType
32 class ClosurizedNamespacesInfoTest(googletest.TestCase):
33 """Tests for ClosurizedNamespacesInfo."""
36 'goog.global.anything': None,
37 'package.CONSTANT': 'package',
38 'package.methodName': 'package',
39 'package.subpackage.methodName': 'package.subpackage',
40 'package.subpackage.methodName.apply': 'package.subpackage',
41 'package.ClassName.something': 'package.ClassName',
42 'package.ClassName.Enum.VALUE.methodName': 'package.ClassName',
43 'package.ClassName.CONSTANT': 'package.ClassName',
44 'package.namespace.CONSTANT.methodName': 'package.namespace',
45 'package.ClassName.inherits': 'package.ClassName',
46 'package.ClassName.apply': 'package.ClassName',
47 'package.ClassName.methodName.apply': 'package.ClassName',
48 'package.ClassName.methodName.call': 'package.ClassName',
49 'package.ClassName.prototype.methodName': 'package.ClassName',
50 'package.ClassName.privateMethod_': 'package.ClassName',
51 'package.className.privateProperty_': 'package.className',
52 'package.className.privateProperty_.methodName': 'package.className',
53 'package.ClassName.PrivateEnum_': 'package.ClassName',
54 'package.ClassName.prototype.methodName.apply': 'package.ClassName',
55 'package.ClassName.property.subProperty': 'package.ClassName',
56 'package.className.prototype.something.somethingElse': 'package.className'
59 _tokenizer = javascripttokenizer.JavaScriptTokenizer()
61 def testGetClosurizedNamespace(self):
62 """Tests that the correct namespace is returned for various identifiers."""
63 namespaces_info = closurizednamespacesinfo.ClosurizedNamespacesInfo(
64 closurized_namespaces=['package'], ignored_extra_namespaces=[])
65 for identifier, expected_namespace in self._test_cases.items():
66 actual_namespace = namespaces_info.GetClosurizedNamespace(identifier)
70 'expected namespace "' + str(expected_namespace) +
71 '" for identifier "' + str(identifier) + '" but was "' +
72 str(actual_namespace) + '"')
74 def testIgnoredExtraNamespaces(self):
75 """Tests that ignored_extra_namespaces are ignored."""
76 token = self._GetRequireTokens('package.Something')
77 namespaces_info = closurizednamespacesinfo.ClosurizedNamespacesInfo(
78 closurized_namespaces=['package'],
79 ignored_extra_namespaces=['package.Something'])
81 self.assertFalse(namespaces_info.IsExtraRequire(token),
82 'Should be valid since it is in ignored namespaces.')
84 namespaces_info = closurizednamespacesinfo.ClosurizedNamespacesInfo(
87 self.assertTrue(namespaces_info.IsExtraRequire(token),
88 'Should be invalid since it is not in ignored namespaces.')
90 def testIsExtraProvide_created(self):
91 """Tests that provides for created namespaces are not extra."""
93 'goog.provide(\'package.Foo\');',
94 'package.Foo = function() {};'
96 token = self._tokenizer.TokenizeFile(input_lines)
97 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
99 self.assertFalse(namespaces_info.IsExtraProvide(token),
100 'Should not be extra since it is created.')
102 def testIsExtraProvide_createdIdentifier(self):
103 """Tests that provides for created identifiers are not extra."""
105 'goog.provide(\'package.Foo.methodName\');',
106 'package.Foo.methodName = function() {};'
108 token = self._tokenizer.TokenizeFile(input_lines)
109 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
111 self.assertFalse(namespaces_info.IsExtraProvide(token),
112 'Should not be extra since it is created.')
114 def testIsExtraProvide_notCreated(self):
115 """Tests that provides for non-created namespaces are extra."""
116 input_lines = ['goog.provide(\'package.Foo\');']
117 token = self._tokenizer.TokenizeFile(input_lines)
118 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
120 self.assertTrue(namespaces_info.IsExtraProvide(token),
121 'Should be extra since it is not created.')
123 def testIsExtraProvide_duplicate(self):
124 """Tests that providing a namespace twice makes the second one extra."""
126 'goog.provide(\'package.Foo\');',
127 'goog.provide(\'package.Foo\');',
128 'package.Foo = function() {};'
130 token = self._tokenizer.TokenizeFile(input_lines)
131 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
133 # Advance to the second goog.provide token.
134 token = tokenutil.Search(token.next, TokenType.IDENTIFIER)
136 self.assertTrue(namespaces_info.IsExtraProvide(token),
137 'Should be extra since it is already provided.')
139 def testIsExtraProvide_notClosurized(self):
140 """Tests that provides of non-closurized namespaces are not extra."""
141 input_lines = ['goog.provide(\'notclosurized.Foo\');']
142 token = self._tokenizer.TokenizeFile(input_lines)
143 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
145 self.assertFalse(namespaces_info.IsExtraProvide(token),
146 'Should not be extra since it is not closurized.')
148 def testIsExtraRequire_used(self):
149 """Tests that requires for used namespaces are not extra."""
151 'goog.require(\'package.Foo\');',
152 'var x = package.Foo.methodName();'
154 token = self._tokenizer.TokenizeFile(input_lines)
155 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
157 self.assertFalse(namespaces_info.IsExtraRequire(token),
158 'Should not be extra since it is used.')
160 def testIsExtraRequire_usedIdentifier(self):
161 """Tests that requires for used methods on classes are extra."""
163 'goog.require(\'package.Foo.methodName\');',
164 'var x = package.Foo.methodName();'
166 token = self._tokenizer.TokenizeFile(input_lines)
167 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
169 self.assertTrue(namespaces_info.IsExtraRequire(token),
170 'Should require the package, not the method specifically.')
172 def testIsExtraRequire_notUsed(self):
173 """Tests that requires for unused namespaces are extra."""
174 input_lines = ['goog.require(\'package.Foo\');']
175 token = self._tokenizer.TokenizeFile(input_lines)
176 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
178 self.assertTrue(namespaces_info.IsExtraRequire(token),
179 'Should be extra since it is not used.')
181 def testIsExtraRequire_notClosurized(self):
182 """Tests that requires of non-closurized namespaces are not extra."""
183 input_lines = ['goog.require(\'notclosurized.Foo\');']
184 token = self._tokenizer.TokenizeFile(input_lines)
185 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
187 self.assertFalse(namespaces_info.IsExtraRequire(token),
188 'Should not be extra since it is not closurized.')
190 def testIsExtraRequire_objectOnClass(self):
191 """Tests that requiring an object on a class is extra."""
193 'goog.require(\'package.Foo.Enum\');',
194 'var x = package.Foo.Enum.VALUE1;',
196 token = self._tokenizer.TokenizeFile(input_lines)
197 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
199 self.assertTrue(namespaces_info.IsExtraRequire(token),
200 'The whole class, not the object, should be required.');
202 def testIsExtraRequire_constantOnClass(self):
203 """Tests that requiring a constant on a class is extra."""
205 'goog.require(\'package.Foo.CONSTANT\');',
206 'var x = package.Foo.CONSTANT',
208 token = self._tokenizer.TokenizeFile(input_lines)
209 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
211 self.assertTrue(namespaces_info.IsExtraRequire(token),
212 'The class, not the constant, should be required.');
214 def testIsExtraRequire_constantNotOnClass(self):
215 """Tests that requiring a constant not on a class is OK."""
217 'goog.require(\'package.subpackage.CONSTANT\');',
218 'var x = package.subpackage.CONSTANT',
220 token = self._tokenizer.TokenizeFile(input_lines)
221 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
223 self.assertFalse(namespaces_info.IsExtraRequire(token),
224 'Constants can be required except on classes.');
226 def testIsExtraRequire_methodNotOnClass(self):
227 """Tests that requiring a method not on a class is OK."""
229 'goog.require(\'package.subpackage.method\');',
230 'var x = package.subpackage.method()',
232 token = self._tokenizer.TokenizeFile(input_lines)
233 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
235 self.assertFalse(namespaces_info.IsExtraRequire(token),
236 'Methods can be required except on classes.');
238 def testIsExtraRequire_defaults(self):
239 """Tests that there are no warnings about extra requires for test utils"""
240 input_lines = ['goog.require(\'goog.testing.jsunit\');']
241 token = self._tokenizer.TokenizeFile(input_lines)
242 namespaces_info = self._GetInitializedNamespacesInfo(token, ['goog'], [])
244 self.assertFalse(namespaces_info.IsExtraRequire(token),
245 'Should not be extra since it is for testing.')
247 def testGetMissingProvides_provided(self):
248 """Tests that provided functions don't cause a missing provide."""
250 'goog.provide(\'package.Foo\');',
251 'package.Foo = function() {};'
253 token = self._tokenizer.TokenizeFile(input_lines)
254 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
256 self.assertEquals(0, len(namespaces_info.GetMissingProvides()))
258 def testGetMissingProvides_providedIdentifier(self):
259 """Tests that provided identifiers don't cause a missing provide."""
261 'goog.provide(\'package.Foo.methodName\');',
262 'package.Foo.methodName = function() {};'
264 token = self._tokenizer.TokenizeFile(input_lines)
265 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
267 self.assertEquals(0, len(namespaces_info.GetMissingProvides()))
269 def testGetMissingProvides_providedParentIdentifier(self):
270 """Tests that provided identifiers on a class don't cause a missing provide
271 on objects attached to that class."""
273 'goog.provide(\'package.foo.ClassName\');',
274 'package.foo.ClassName.methodName = function() {};',
275 'package.foo.ClassName.ObjectName = 1;',
277 token = self._tokenizer.TokenizeFile(input_lines)
278 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
280 self.assertEquals(0, len(namespaces_info.GetMissingProvides()))
282 def testGetMissingProvides_unprovided(self):
283 """Tests that unprovided functions cause a missing provide."""
284 input_lines = ['package.Foo = function() {};']
285 token = self._tokenizer.TokenizeFile(input_lines)
286 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
288 self.assertEquals(1, len(namespaces_info.GetMissingProvides()))
289 self.assertTrue('package.Foo' in namespaces_info.GetMissingProvides())
291 def testGetMissingProvides_privatefunction(self):
292 """Tests that unprovided private functions don't cause a missing provide."""
293 input_lines = ['package.Foo_ = function() {};']
294 token = self._tokenizer.TokenizeFile(input_lines)
295 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
297 self.assertEquals(0, len(namespaces_info.GetMissingProvides()))
299 def testGetMissingProvides_required(self):
300 """Tests that required namespaces don't cause a missing provide."""
302 'goog.require(\'package.Foo\');',
303 'package.Foo.methodName = function() {};'
305 token = self._tokenizer.TokenizeFile(input_lines)
306 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
308 self.assertEquals(0, len(namespaces_info.GetMissingProvides()))
310 def testGetMissingRequires_required(self):
311 """Tests that required namespaces don't cause a missing require."""
313 'goog.require(\'package.Foo\');',
316 token = self._tokenizer.TokenizeFile(input_lines)
317 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
319 self.assertEquals(0, len(namespaces_info.GetMissingProvides()))
321 def testGetMissingRequires_requiredIdentifier(self):
322 """Tests that required namespaces satisfy identifiers on that namespace."""
324 'goog.require(\'package.Foo\');',
325 'package.Foo.methodName();'
327 token = self._tokenizer.TokenizeFile(input_lines)
328 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
330 self.assertEquals(0, len(namespaces_info.GetMissingProvides()))
332 def testGetMissingRequires_requiredParentClass(self):
333 """Tests that requiring a parent class of an object is sufficient to prevent
334 a missing require on that object."""
336 'goog.require(\'package.Foo\');',
337 'package.Foo.methodName();',
338 'package.Foo.methodName(package.Foo.ObjectName);'
340 token = self._tokenizer.TokenizeFile(input_lines)
341 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
343 self.assertEquals(0, len(namespaces_info.GetMissingRequires()))
345 def testGetMissingRequires_unrequired(self):
346 """Tests that unrequired namespaces cause a missing require."""
347 input_lines = ['package.Foo();']
348 token = self._tokenizer.TokenizeFile(input_lines)
349 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
351 self.assertEquals(1, len(namespaces_info.GetMissingRequires()))
352 self.assertTrue('package.Foo' in namespaces_info.GetMissingRequires())
354 def testGetMissingRequires_provided(self):
355 """Tests that provided namespaces satisfy identifiers on that namespace."""
357 'goog.provide(\'package.Foo\');',
358 'package.Foo.methodName();'
360 token = self._tokenizer.TokenizeFile(input_lines)
361 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
363 self.assertEquals(0, len(namespaces_info.GetMissingRequires()))
365 def testGetMissingRequires_created(self):
366 """Tests that created namespaces do not satisfy usage of an identifier."""
368 'package.Foo = function();',
369 'package.Foo.methodName();'
371 token = self._tokenizer.TokenizeFile(input_lines)
372 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
374 self.assertEquals(1, len(namespaces_info.GetMissingRequires()))
375 self.assertTrue('package.Foo' in namespaces_info.GetMissingRequires())
377 def testGetMissingRequires_createdIdentifier(self):
378 """Tests that created identifiers satisfy usage of the identifier."""
380 'package.Foo.methodName = function();',
381 'package.Foo.methodName();'
383 token = self._tokenizer.TokenizeFile(input_lines)
384 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
386 self.assertEquals(0, len(namespaces_info.GetMissingRequires()))
388 def testGetMissingRequires_objectOnClass(self):
389 """Tests that we should require a class, not the object on the class."""
391 'goog.require(\'package.Foo.Enum\');',
392 'var x = package.Foo.Enum.VALUE1;',
394 token = self._tokenizer.TokenizeFile(input_lines)
395 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
397 self.assertEquals(1, len(namespaces_info.GetMissingRequires()),
398 'The whole class, not the object, should be required.');
400 def testIsFirstProvide(self):
401 """Tests operation of the isFirstProvide method."""
403 'goog.provide(\'package.Foo\');',
404 'package.Foo.methodName();'
406 token = self._tokenizer.TokenizeFile(input_lines)
407 namespaces_info = self._GetInitializedNamespacesInfo(token, ['package'], [])
409 self.assertTrue(namespaces_info.IsFirstProvide(token))
411 def testGetWholeIdentifierString(self):
412 """Tests that created identifiers satisfy usage of the identifier."""
418 token = self._tokenizer.TokenizeFile(input_lines)
419 namespaces_info = closurizednamespacesinfo.ClosurizedNamespacesInfo([], [])
421 self.assertEquals('package.Foo.veryLong.identifier',
422 namespaces_info._GetWholeIdentifierString(token))
423 self.assertEquals(None,
424 namespaces_info._GetWholeIdentifierString(token.next))
426 def _GetInitializedNamespacesInfo(self, token, closurized_namespaces,
427 ignored_extra_namespaces):
428 """Returns a namespaces info initialized with the given token stream."""
429 namespaces_info = closurizednamespacesinfo.ClosurizedNamespacesInfo(
430 closurized_namespaces=closurized_namespaces,
431 ignored_extra_namespaces=ignored_extra_namespaces)
432 state_tracker = javascriptstatetracker.JavaScriptStateTracker()
435 namespaces_info.ProcessToken(token, state_tracker)
438 return namespaces_info
440 def _GetProvideTokens(self, namespace):
441 """Returns a list of tokens for a goog.require of the given namespace."""
442 line_text = 'goog.require(\'' + namespace + '\');\n'
443 return javascripttokenizer.JavaScriptTokenizer().TokenizeFile([line_text])
445 def _GetRequireTokens(self, namespace):
446 """Returns a list of tokens for a goog.require of the given namespace."""
447 line_text = 'goog.require(\'' + namespace + '\');\n'
448 return javascripttokenizer.JavaScriptTokenizer().TokenizeFile([line_text])
450 if __name__ == '__main__':