| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # | |
| 3 # Copyright 2010 The Closure Linter Authors. All Rights Reserved. | |
| 4 # | |
| 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 | |
| 8 # | |
| 9 # http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 # | |
| 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. | |
| 16 | |
| 17 """Unit tests for ClosurizedNamespacesInfo.""" | |
| 18 | |
| 19 | |
| 20 | |
| 21 import unittest as googletest | |
| 22 from closure_linter import aliaspass | |
| 23 from closure_linter import closurizednamespacesinfo | |
| 24 from closure_linter import ecmametadatapass | |
| 25 from closure_linter import javascriptstatetracker | |
| 26 from closure_linter import javascripttokens | |
| 27 from closure_linter import testutil | |
| 28 from closure_linter import tokenutil | |
| 29 | |
| 30 # pylint: disable=g-bad-name | |
| 31 TokenType = javascripttokens.JavaScriptTokenType | |
| 32 | |
| 33 | |
| 34 def _ToLineDict(illegal_alias_stmts): | |
| 35 """Replaces tokens with the respective line number.""" | |
| 36 return {k: v.line_number for k, v in illegal_alias_stmts.iteritems()} | |
| 37 | |
| 38 | |
| 39 class ClosurizedNamespacesInfoTest(googletest.TestCase): | |
| 40 """Tests for ClosurizedNamespacesInfo.""" | |
| 41 | |
| 42 _test_cases = { | |
| 43 'goog.global.anything': None, | |
| 44 'package.CONSTANT': 'package', | |
| 45 'package.methodName': 'package', | |
| 46 'package.subpackage.methodName': 'package.subpackage', | |
| 47 'package.subpackage.methodName.apply': 'package.subpackage', | |
| 48 'package.ClassName.something': 'package.ClassName', | |
| 49 'package.ClassName.Enum.VALUE.methodName': 'package.ClassName', | |
| 50 'package.ClassName.CONSTANT': 'package.ClassName', | |
| 51 'package.namespace.CONSTANT.methodName': 'package.namespace', | |
| 52 'package.ClassName.inherits': 'package.ClassName', | |
| 53 'package.ClassName.apply': 'package.ClassName', | |
| 54 'package.ClassName.methodName.apply': 'package.ClassName', | |
| 55 'package.ClassName.methodName.call': 'package.ClassName', | |
| 56 'package.ClassName.prototype.methodName': 'package.ClassName', | |
| 57 'package.ClassName.privateMethod_': 'package.ClassName', | |
| 58 'package.className.privateProperty_': 'package.className', | |
| 59 'package.className.privateProperty_.methodName': 'package.className', | |
| 60 'package.ClassName.PrivateEnum_': 'package.ClassName', | |
| 61 'package.ClassName.prototype.methodName.apply': 'package.ClassName', | |
| 62 'package.ClassName.property.subProperty': 'package.ClassName', | |
| 63 'package.className.prototype.something.somethingElse': 'package.className' | |
| 64 } | |
| 65 | |
| 66 def testGetClosurizedNamespace(self): | |
| 67 """Tests that the correct namespace is returned for various identifiers.""" | |
| 68 namespaces_info = closurizednamespacesinfo.ClosurizedNamespacesInfo( | |
| 69 closurized_namespaces=['package'], ignored_extra_namespaces=[]) | |
| 70 for identifier, expected_namespace in self._test_cases.items(): | |
| 71 actual_namespace = namespaces_info.GetClosurizedNamespace(identifier) | |
| 72 self.assertEqual( | |
| 73 expected_namespace, | |
| 74 actual_namespace, | |
| 75 'expected namespace "' + str(expected_namespace) + | |
| 76 '" for identifier "' + str(identifier) + '" but was "' + | |
| 77 str(actual_namespace) + '"') | |
| 78 | |
| 79 def testIgnoredExtraNamespaces(self): | |
| 80 """Tests that ignored_extra_namespaces are ignored.""" | |
| 81 token = self._GetRequireTokens('package.Something') | |
| 82 namespaces_info = closurizednamespacesinfo.ClosurizedNamespacesInfo( | |
| 83 closurized_namespaces=['package'], | |
| 84 ignored_extra_namespaces=['package.Something']) | |
| 85 | |
| 86 self.assertFalse(namespaces_info.IsExtraRequire(token), | |
| 87 'Should be valid since it is in ignored namespaces.') | |
| 88 | |
| 89 namespaces_info = closurizednamespacesinfo.ClosurizedNamespacesInfo( | |
| 90 ['package'], []) | |
| 91 | |
| 92 self.assertTrue(namespaces_info.IsExtraRequire(token), | |
| 93 'Should be invalid since it is not in ignored namespaces.') | |
| 94 | |
| 95 def testIsExtraProvide_created(self): | |
| 96 """Tests that provides for created namespaces are not extra.""" | |
| 97 input_lines = [ | |
| 98 'goog.provide(\'package.Foo\');', | |
| 99 'package.Foo = function() {};' | |
| 100 ] | |
| 101 | |
| 102 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 103 input_lines, ['package']) | |
| 104 | |
| 105 self.assertFalse(namespaces_info.IsExtraProvide(token), | |
| 106 'Should not be extra since it is created.') | |
| 107 | |
| 108 def testIsExtraProvide_createdIdentifier(self): | |
| 109 """Tests that provides for created identifiers are not extra.""" | |
| 110 input_lines = [ | |
| 111 'goog.provide(\'package.Foo.methodName\');', | |
| 112 'package.Foo.methodName = function() {};' | |
| 113 ] | |
| 114 | |
| 115 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 116 input_lines, ['package']) | |
| 117 | |
| 118 self.assertFalse(namespaces_info.IsExtraProvide(token), | |
| 119 'Should not be extra since it is created.') | |
| 120 | |
| 121 def testIsExtraProvide_notCreated(self): | |
| 122 """Tests that provides for non-created namespaces are extra.""" | |
| 123 input_lines = ['goog.provide(\'package.Foo\');'] | |
| 124 | |
| 125 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 126 input_lines, ['package']) | |
| 127 | |
| 128 self.assertTrue(namespaces_info.IsExtraProvide(token), | |
| 129 'Should be extra since it is not created.') | |
| 130 | |
| 131 def testIsExtraProvide_notCreatedMultipartClosurizedNamespace(self): | |
| 132 """Tests that provides for non-created namespaces are extra.""" | |
| 133 input_lines = ['goog.provide(\'multi.part.namespace.Foo\');'] | |
| 134 | |
| 135 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 136 input_lines, ['multi.part']) | |
| 137 | |
| 138 self.assertTrue(namespaces_info.IsExtraProvide(token), | |
| 139 'Should be extra since it is not created.') | |
| 140 | |
| 141 def testIsExtraProvide_duplicate(self): | |
| 142 """Tests that providing a namespace twice makes the second one extra.""" | |
| 143 input_lines = [ | |
| 144 'goog.provide(\'package.Foo\');', | |
| 145 'goog.provide(\'package.Foo\');', | |
| 146 'package.Foo = function() {};' | |
| 147 ] | |
| 148 | |
| 149 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 150 input_lines, ['package']) | |
| 151 | |
| 152 # Advance to the second goog.provide token. | |
| 153 token = tokenutil.Search(token.next, TokenType.IDENTIFIER) | |
| 154 | |
| 155 self.assertTrue(namespaces_info.IsExtraProvide(token), | |
| 156 'Should be extra since it is already provided.') | |
| 157 | |
| 158 def testIsExtraProvide_notClosurized(self): | |
| 159 """Tests that provides of non-closurized namespaces are not extra.""" | |
| 160 input_lines = ['goog.provide(\'notclosurized.Foo\');'] | |
| 161 | |
| 162 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 163 input_lines, ['package']) | |
| 164 | |
| 165 self.assertFalse(namespaces_info.IsExtraProvide(token), | |
| 166 'Should not be extra since it is not closurized.') | |
| 167 | |
| 168 def testIsExtraRequire_used(self): | |
| 169 """Tests that requires for used namespaces are not extra.""" | |
| 170 input_lines = [ | |
| 171 'goog.require(\'package.Foo\');', | |
| 172 'var x = package.Foo.methodName();' | |
| 173 ] | |
| 174 | |
| 175 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 176 input_lines, ['package']) | |
| 177 | |
| 178 self.assertFalse(namespaces_info.IsExtraRequire(token), | |
| 179 'Should not be extra since it is used.') | |
| 180 | |
| 181 def testIsExtraRequire_usedIdentifier(self): | |
| 182 """Tests that requires for used methods on classes are extra.""" | |
| 183 input_lines = [ | |
| 184 'goog.require(\'package.Foo.methodName\');', | |
| 185 'var x = package.Foo.methodName();' | |
| 186 ] | |
| 187 | |
| 188 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 189 input_lines, ['package']) | |
| 190 | |
| 191 self.assertTrue(namespaces_info.IsExtraRequire(token), | |
| 192 'Should require the package, not the method specifically.') | |
| 193 | |
| 194 def testIsExtraRequire_notUsed(self): | |
| 195 """Tests that requires for unused namespaces are extra.""" | |
| 196 input_lines = ['goog.require(\'package.Foo\');'] | |
| 197 | |
| 198 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 199 input_lines, ['package']) | |
| 200 | |
| 201 self.assertTrue(namespaces_info.IsExtraRequire(token), | |
| 202 'Should be extra since it is not used.') | |
| 203 | |
| 204 def testIsExtraRequire_notUsedMultiPartClosurizedNamespace(self): | |
| 205 """Tests unused require with multi-part closurized namespaces.""" | |
| 206 | |
| 207 input_lines = ['goog.require(\'multi.part.namespace.Foo\');'] | |
| 208 | |
| 209 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 210 input_lines, ['multi.part']) | |
| 211 | |
| 212 self.assertTrue(namespaces_info.IsExtraRequire(token), | |
| 213 'Should be extra since it is not used.') | |
| 214 | |
| 215 def testIsExtraRequire_notClosurized(self): | |
| 216 """Tests that requires of non-closurized namespaces are not extra.""" | |
| 217 input_lines = ['goog.require(\'notclosurized.Foo\');'] | |
| 218 | |
| 219 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 220 input_lines, ['package']) | |
| 221 | |
| 222 self.assertFalse(namespaces_info.IsExtraRequire(token), | |
| 223 'Should not be extra since it is not closurized.') | |
| 224 | |
| 225 def testIsExtraRequire_objectOnClass(self): | |
| 226 """Tests that requiring an object on a class is extra.""" | |
| 227 input_lines = [ | |
| 228 'goog.require(\'package.Foo.Enum\');', | |
| 229 'var x = package.Foo.Enum.VALUE1;', | |
| 230 ] | |
| 231 | |
| 232 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 233 input_lines, ['package']) | |
| 234 | |
| 235 self.assertTrue(namespaces_info.IsExtraRequire(token), | |
| 236 'The whole class, not the object, should be required.'); | |
| 237 | |
| 238 def testIsExtraRequire_constantOnClass(self): | |
| 239 """Tests that requiring a constant on a class is extra.""" | |
| 240 input_lines = [ | |
| 241 'goog.require(\'package.Foo.CONSTANT\');', | |
| 242 'var x = package.Foo.CONSTANT', | |
| 243 ] | |
| 244 | |
| 245 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 246 input_lines, ['package']) | |
| 247 | |
| 248 self.assertTrue(namespaces_info.IsExtraRequire(token), | |
| 249 'The class, not the constant, should be required.'); | |
| 250 | |
| 251 def testIsExtraRequire_constantNotOnClass(self): | |
| 252 """Tests that requiring a constant not on a class is OK.""" | |
| 253 input_lines = [ | |
| 254 'goog.require(\'package.subpackage.CONSTANT\');', | |
| 255 'var x = package.subpackage.CONSTANT', | |
| 256 ] | |
| 257 | |
| 258 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 259 input_lines, ['package']) | |
| 260 | |
| 261 self.assertFalse(namespaces_info.IsExtraRequire(token), | |
| 262 'Constants can be required except on classes.'); | |
| 263 | |
| 264 def testIsExtraRequire_methodNotOnClass(self): | |
| 265 """Tests that requiring a method not on a class is OK.""" | |
| 266 input_lines = [ | |
| 267 'goog.require(\'package.subpackage.method\');', | |
| 268 'var x = package.subpackage.method()', | |
| 269 ] | |
| 270 | |
| 271 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 272 input_lines, ['package']) | |
| 273 | |
| 274 self.assertFalse(namespaces_info.IsExtraRequire(token), | |
| 275 'Methods can be required except on classes.'); | |
| 276 | |
| 277 def testIsExtraRequire_defaults(self): | |
| 278 """Tests that there are no warnings about extra requires for test utils""" | |
| 279 input_lines = ['goog.require(\'goog.testing.jsunit\');'] | |
| 280 | |
| 281 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 282 input_lines, ['goog']) | |
| 283 | |
| 284 self.assertFalse(namespaces_info.IsExtraRequire(token), | |
| 285 'Should not be extra since it is for testing.') | |
| 286 | |
| 287 def testGetMissingProvides_provided(self): | |
| 288 """Tests that provided functions don't cause a missing provide.""" | |
| 289 input_lines = [ | |
| 290 'goog.provide(\'package.Foo\');', | |
| 291 'package.Foo = function() {};' | |
| 292 ] | |
| 293 | |
| 294 namespaces_info = self._GetNamespacesInfoForScript( | |
| 295 input_lines, ['package']) | |
| 296 | |
| 297 self.assertEquals(0, len(namespaces_info.GetMissingProvides())) | |
| 298 | |
| 299 def testGetMissingProvides_providedIdentifier(self): | |
| 300 """Tests that provided identifiers don't cause a missing provide.""" | |
| 301 input_lines = [ | |
| 302 'goog.provide(\'package.Foo.methodName\');', | |
| 303 'package.Foo.methodName = function() {};' | |
| 304 ] | |
| 305 | |
| 306 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 307 self.assertEquals(0, len(namespaces_info.GetMissingProvides())) | |
| 308 | |
| 309 def testGetMissingProvides_providedParentIdentifier(self): | |
| 310 """Tests that provided identifiers on a class don't cause a missing provide | |
| 311 on objects attached to that class.""" | |
| 312 input_lines = [ | |
| 313 'goog.provide(\'package.foo.ClassName\');', | |
| 314 'package.foo.ClassName.methodName = function() {};', | |
| 315 'package.foo.ClassName.ObjectName = 1;', | |
| 316 ] | |
| 317 | |
| 318 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 319 self.assertEquals(0, len(namespaces_info.GetMissingProvides())) | |
| 320 | |
| 321 def testGetMissingProvides_unprovided(self): | |
| 322 """Tests that unprovided functions cause a missing provide.""" | |
| 323 input_lines = ['package.Foo = function() {};'] | |
| 324 | |
| 325 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 326 | |
| 327 missing_provides = namespaces_info.GetMissingProvides() | |
| 328 self.assertEquals(1, len(missing_provides)) | |
| 329 missing_provide = missing_provides.popitem() | |
| 330 self.assertEquals('package.Foo', missing_provide[0]) | |
| 331 self.assertEquals(1, missing_provide[1]) | |
| 332 | |
| 333 def testGetMissingProvides_privatefunction(self): | |
| 334 """Tests that unprovided private functions don't cause a missing provide.""" | |
| 335 input_lines = ['package.Foo_ = function() {};'] | |
| 336 | |
| 337 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 338 self.assertEquals(0, len(namespaces_info.GetMissingProvides())) | |
| 339 | |
| 340 def testGetMissingProvides_required(self): | |
| 341 """Tests that required namespaces don't cause a missing provide.""" | |
| 342 input_lines = [ | |
| 343 'goog.require(\'package.Foo\');', | |
| 344 'package.Foo.methodName = function() {};' | |
| 345 ] | |
| 346 | |
| 347 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 348 self.assertEquals(0, len(namespaces_info.GetMissingProvides())) | |
| 349 | |
| 350 def testGetMissingRequires_required(self): | |
| 351 """Tests that required namespaces don't cause a missing require.""" | |
| 352 input_lines = [ | |
| 353 'goog.require(\'package.Foo\');', | |
| 354 'package.Foo();' | |
| 355 ] | |
| 356 | |
| 357 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 358 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 359 self.assertEquals(0, len(missing_requires)) | |
| 360 | |
| 361 def testGetMissingRequires_requiredIdentifier(self): | |
| 362 """Tests that required namespaces satisfy identifiers on that namespace.""" | |
| 363 input_lines = [ | |
| 364 'goog.require(\'package.Foo\');', | |
| 365 'package.Foo.methodName();' | |
| 366 ] | |
| 367 | |
| 368 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 369 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 370 self.assertEquals(0, len(missing_requires)) | |
| 371 | |
| 372 def testGetMissingRequires_requiredNamespace(self): | |
| 373 """Tests that required namespaces satisfy the namespace.""" | |
| 374 input_lines = [ | |
| 375 'goog.require(\'package.soy.fooTemplate\');', | |
| 376 'render(package.soy.fooTemplate);' | |
| 377 ] | |
| 378 | |
| 379 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 380 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 381 self.assertEquals(0, len(missing_requires)) | |
| 382 | |
| 383 def testGetMissingRequires_requiredParentClass(self): | |
| 384 """Tests that requiring a parent class of an object is sufficient to prevent | |
| 385 a missing require on that object.""" | |
| 386 input_lines = [ | |
| 387 'goog.require(\'package.Foo\');', | |
| 388 'package.Foo.methodName();', | |
| 389 'package.Foo.methodName(package.Foo.ObjectName);' | |
| 390 ] | |
| 391 | |
| 392 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 393 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 394 self.assertEquals(0, len(missing_requires)) | |
| 395 | |
| 396 def testGetMissingRequires_unrequired(self): | |
| 397 """Tests that unrequired namespaces cause a missing require.""" | |
| 398 input_lines = ['package.Foo();'] | |
| 399 | |
| 400 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 401 | |
| 402 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 403 self.assertEquals(1, len(missing_requires)) | |
| 404 missing_req = missing_requires.popitem() | |
| 405 self.assertEquals('package.Foo', missing_req[0]) | |
| 406 self.assertEquals(1, missing_req[1]) | |
| 407 | |
| 408 def testGetMissingRequires_provided(self): | |
| 409 """Tests that provided namespaces satisfy identifiers on that namespace.""" | |
| 410 input_lines = [ | |
| 411 'goog.provide(\'package.Foo\');', | |
| 412 'package.Foo.methodName();' | |
| 413 ] | |
| 414 | |
| 415 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 416 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 417 self.assertEquals(0, len(missing_requires)) | |
| 418 | |
| 419 def testGetMissingRequires_created(self): | |
| 420 """Tests that created namespaces do not satisfy usage of an identifier.""" | |
| 421 input_lines = [ | |
| 422 'package.Foo = function();', | |
| 423 'package.Foo.methodName();', | |
| 424 'package.Foo.anotherMethodName1();', | |
| 425 'package.Foo.anotherMethodName2();' | |
| 426 ] | |
| 427 | |
| 428 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 429 | |
| 430 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 431 self.assertEquals(1, len(missing_requires)) | |
| 432 missing_require = missing_requires.popitem() | |
| 433 self.assertEquals('package.Foo', missing_require[0]) | |
| 434 # Make sure line number of first occurrence is reported | |
| 435 self.assertEquals(2, missing_require[1]) | |
| 436 | |
| 437 def testGetMissingRequires_createdIdentifier(self): | |
| 438 """Tests that created identifiers satisfy usage of the identifier.""" | |
| 439 input_lines = [ | |
| 440 'package.Foo.methodName = function();', | |
| 441 'package.Foo.methodName();' | |
| 442 ] | |
| 443 | |
| 444 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 445 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 446 self.assertEquals(0, len(missing_requires)) | |
| 447 | |
| 448 def testGetMissingRequires_implements(self): | |
| 449 """Tests that a parametrized type requires the correct identifier.""" | |
| 450 input_lines = [ | |
| 451 '/** @constructor @implements {package.Bar<T>} */', | |
| 452 'package.Foo = function();', | |
| 453 ] | |
| 454 | |
| 455 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 456 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 457 self.assertItemsEqual({'package.Bar': 1}, missing_requires) | |
| 458 | |
| 459 def testGetMissingRequires_objectOnClass(self): | |
| 460 """Tests that we should require a class, not the object on the class.""" | |
| 461 input_lines = [ | |
| 462 'goog.require(\'package.Foo.Enum\');', | |
| 463 'var x = package.Foo.Enum.VALUE1;', | |
| 464 ] | |
| 465 | |
| 466 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['package']) | |
| 467 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 468 self.assertEquals(1, len(missing_requires), | |
| 469 'The whole class, not the object, should be required.') | |
| 470 | |
| 471 def testGetMissingRequires_variableWithSameName(self): | |
| 472 """Tests that we should not goog.require variables and parameters. | |
| 473 | |
| 474 b/5362203 Variables in scope are not missing namespaces. | |
| 475 """ | |
| 476 input_lines = [ | |
| 477 'goog.provide(\'Foo\');', | |
| 478 'Foo.A = function();', | |
| 479 'Foo.A.prototype.method = function(ab) {', | |
| 480 ' if (ab) {', | |
| 481 ' var docs;', | |
| 482 ' var lvalue = new Obj();', | |
| 483 ' // Variable in scope hence not goog.require here.', | |
| 484 ' docs.foo.abc = 1;', | |
| 485 ' lvalue.next();', | |
| 486 ' }', | |
| 487 ' // Since js is function scope this should also not goog.require.', | |
| 488 ' docs.foo.func();', | |
| 489 ' // Its not a variable in scope hence goog.require.', | |
| 490 ' dummy.xyz.reset();', | |
| 491 ' return this.method2();', | |
| 492 '};', | |
| 493 'Foo.A.prototype.method1 = function(docs, abcd, xyz) {', | |
| 494 ' // Parameter hence not goog.require.', | |
| 495 ' docs.nodes.length = 2;', | |
| 496 ' lvalue.abc.reset();', | |
| 497 '};' | |
| 498 ] | |
| 499 | |
| 500 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['Foo', | |
| 501 'docs', | |
| 502 'lvalue', | |
| 503 'dummy']) | |
| 504 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 505 self.assertEquals(2, len(missing_requires)) | |
| 506 self.assertItemsEqual( | |
| 507 {'dummy.xyz': 14, | |
| 508 'lvalue.abc': 20}, missing_requires) | |
| 509 | |
| 510 def testIsFirstProvide(self): | |
| 511 """Tests operation of the isFirstProvide method.""" | |
| 512 input_lines = [ | |
| 513 'goog.provide(\'package.Foo\');', | |
| 514 'package.Foo.methodName();' | |
| 515 ] | |
| 516 | |
| 517 token, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 518 input_lines, ['package']) | |
| 519 self.assertTrue(namespaces_info.IsFirstProvide(token)) | |
| 520 | |
| 521 def testGetWholeIdentifierString(self): | |
| 522 """Tests that created identifiers satisfy usage of the identifier.""" | |
| 523 input_lines = [ | |
| 524 'package.Foo.', | |
| 525 ' veryLong.', | |
| 526 ' identifier;' | |
| 527 ] | |
| 528 | |
| 529 token = testutil.TokenizeSource(input_lines) | |
| 530 | |
| 531 self.assertEquals('package.Foo.veryLong.identifier', | |
| 532 tokenutil.GetIdentifierForToken(token)) | |
| 533 | |
| 534 self.assertEquals(None, | |
| 535 tokenutil.GetIdentifierForToken(token.next)) | |
| 536 | |
| 537 def testScopified(self): | |
| 538 """Tests that a goog.scope call is noticed.""" | |
| 539 input_lines = [ | |
| 540 'goog.scope(function() {', | |
| 541 '});' | |
| 542 ] | |
| 543 | |
| 544 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 545 self.assertTrue(namespaces_info._scopified_file) | |
| 546 | |
| 547 def testScope_unusedAlias(self): | |
| 548 """Tests that an unused alias symbol is illegal.""" | |
| 549 input_lines = [ | |
| 550 'goog.scope(function() {', | |
| 551 'var Event = goog.events.Event;', | |
| 552 '});' | |
| 553 ] | |
| 554 | |
| 555 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 556 missing_requires, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 557 self.assertEquals({}, missing_requires) | |
| 558 self.assertEquals({'goog.events': 2}, _ToLineDict(illegal_alias_stmts)) | |
| 559 | |
| 560 def testScope_usedMultilevelAlias(self): | |
| 561 """Tests that an used alias symbol in a deep namespace is ok.""" | |
| 562 input_lines = [ | |
| 563 'goog.require(\'goog.Events\');', | |
| 564 'goog.scope(function() {', | |
| 565 'var Event = goog.Events.DeepNamespace.Event;', | |
| 566 'Event();', | |
| 567 '});' | |
| 568 ] | |
| 569 | |
| 570 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 571 missing_requires, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 572 self.assertEquals({}, missing_requires) | |
| 573 self.assertEquals({}, illegal_alias_stmts) | |
| 574 | |
| 575 def testScope_usedAlias(self): | |
| 576 """Tests that aliased symbols result in correct requires.""" | |
| 577 input_lines = [ | |
| 578 'goog.scope(function() {', | |
| 579 'var Event = goog.events.Event;', | |
| 580 'var dom = goog.dom;', | |
| 581 'Event(dom.classes.get);', | |
| 582 '});' | |
| 583 ] | |
| 584 | |
| 585 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 586 missing_requires, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 587 self.assertEquals({}, illegal_alias_stmts) | |
| 588 self.assertEquals({'goog.dom.classes': 4, 'goog.events.Event': 4}, | |
| 589 missing_requires) | |
| 590 | |
| 591 def testModule_alias(self): | |
| 592 """Tests that goog.module style aliases are supported.""" | |
| 593 input_lines = [ | |
| 594 'goog.module(\'test.module\');', | |
| 595 'var Unused = goog.require(\'goog.Unused\');', | |
| 596 'var AliasedClass = goog.require(\'goog.AliasedClass\');', | |
| 597 'var x = new AliasedClass();', | |
| 598 ] | |
| 599 | |
| 600 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 601 namespaceToken = self._GetRequireTokens('goog.AliasedClass') | |
| 602 self.assertFalse(namespaces_info.IsExtraRequire(namespaceToken), | |
| 603 'AliasedClass should be marked as used') | |
| 604 unusedToken = self._GetRequireTokens('goog.Unused') | |
| 605 self.assertTrue(namespaces_info.IsExtraRequire(unusedToken), | |
| 606 'Unused should be marked as not used') | |
| 607 | |
| 608 def testModule_aliasInScope(self): | |
| 609 """Tests that goog.module style aliases are supported.""" | |
| 610 input_lines = [ | |
| 611 'goog.module(\'test.module\');', | |
| 612 'var AliasedClass = goog.require(\'goog.AliasedClass\');', | |
| 613 'goog.scope(function() {', | |
| 614 'var x = new AliasedClass();', | |
| 615 '});', | |
| 616 ] | |
| 617 | |
| 618 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 619 namespaceToken = self._GetRequireTokens('goog.AliasedClass') | |
| 620 self.assertFalse(namespaces_info.IsExtraRequire(namespaceToken), | |
| 621 'AliasedClass should be marked as used') | |
| 622 | |
| 623 def testModule_getAlwaysProvided(self): | |
| 624 """Tests that goog.module.get is recognized as a built-in.""" | |
| 625 input_lines = [ | |
| 626 'goog.provide(\'test.MyClass\');', | |
| 627 'goog.require(\'goog.someModule\');', | |
| 628 'goog.scope(function() {', | |
| 629 'var someModule = goog.module.get(\'goog.someModule\');', | |
| 630 'test.MyClass = function() {};', | |
| 631 '});', | |
| 632 ] | |
| 633 | |
| 634 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 635 self.assertEquals({}, namespaces_info.GetMissingRequires()[0]) | |
| 636 | |
| 637 def testModule_requireForGet(self): | |
| 638 """Tests that goog.module.get needs a goog.require call.""" | |
| 639 input_lines = [ | |
| 640 'goog.provide(\'test.MyClass\');', | |
| 641 'function foo() {', | |
| 642 ' var someModule = goog.module.get(\'goog.someModule\');', | |
| 643 ' someModule.doSth();', | |
| 644 '}', | |
| 645 ] | |
| 646 | |
| 647 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 648 self.assertEquals({'goog.someModule': 3}, | |
| 649 namespaces_info.GetMissingRequires()[0]) | |
| 650 | |
| 651 def testScope_usedTypeAlias(self): | |
| 652 """Tests aliased symbols in type annotations.""" | |
| 653 input_lines = [ | |
| 654 'goog.scope(function() {', | |
| 655 'var Event = goog.events.Event;', | |
| 656 '/** @type {Event} */;', | |
| 657 '});' | |
| 658 ] | |
| 659 | |
| 660 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 661 missing_requires, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 662 self.assertEquals({}, missing_requires) | |
| 663 self.assertEquals({'goog.events': 2}, _ToLineDict(illegal_alias_stmts)) | |
| 664 | |
| 665 def testScope_partialAlias_typeOnly(self): | |
| 666 """Tests a partial alias only used in type annotations. | |
| 667 | |
| 668 In this example, some goog.events namespace would need to be required | |
| 669 so that evaluating goog.events.bar doesn't throw an error. | |
| 670 """ | |
| 671 input_lines = [ | |
| 672 'goog.scope(function() {', | |
| 673 'var bar = goog.events.bar;', | |
| 674 '/** @type {bar.Foo} */;', | |
| 675 '});' | |
| 676 ] | |
| 677 | |
| 678 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 679 missing_requires, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 680 self.assertEquals({}, missing_requires) | |
| 681 self.assertEquals({'goog.events': 2}, _ToLineDict(illegal_alias_stmts)) | |
| 682 | |
| 683 def testScope_partialAlias(self): | |
| 684 """Tests a partial alias in conjunction with a type annotation. | |
| 685 | |
| 686 In this example, the partial alias is already defined by another type, | |
| 687 therefore the doc-only type doesn't need to be required. | |
| 688 """ | |
| 689 input_lines = [ | |
| 690 'goog.scope(function() {', | |
| 691 'var bar = goog.events.bar;', | |
| 692 '/** @type {bar.Event} */;', | |
| 693 'bar.EventType();' | |
| 694 '});' | |
| 695 ] | |
| 696 | |
| 697 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 698 missing_requires, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 699 self.assertEquals({'goog.events.bar.EventType': 4}, missing_requires) | |
| 700 self.assertEquals({}, illegal_alias_stmts) | |
| 701 | |
| 702 def testScope_partialAliasRequires(self): | |
| 703 """Tests partial aliases with correct requires.""" | |
| 704 input_lines = [ | |
| 705 'goog.require(\'goog.events.bar.EventType\');', | |
| 706 'goog.scope(function() {', | |
| 707 'var bar = goog.events.bar;', | |
| 708 '/** @type {bar.Event} */;', | |
| 709 'bar.EventType();' | |
| 710 '});' | |
| 711 ] | |
| 712 | |
| 713 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 714 missing_requires, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 715 self.assertEquals({}, missing_requires) | |
| 716 self.assertEquals({}, illegal_alias_stmts) | |
| 717 | |
| 718 def testScope_partialAliasRequiresBoth(self): | |
| 719 """Tests partial aliases with correct requires.""" | |
| 720 input_lines = [ | |
| 721 'goog.require(\'goog.events.bar.Event\');', | |
| 722 'goog.require(\'goog.events.bar.EventType\');', | |
| 723 'goog.scope(function() {', | |
| 724 'var bar = goog.events.bar;', | |
| 725 '/** @type {bar.Event} */;', | |
| 726 'bar.EventType();' | |
| 727 '});' | |
| 728 ] | |
| 729 | |
| 730 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 731 missing_requires, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 732 self.assertEquals({}, missing_requires) | |
| 733 self.assertEquals({}, illegal_alias_stmts) | |
| 734 event_token = self._GetRequireTokens('goog.events.bar.Event') | |
| 735 self.assertTrue(namespaces_info.IsExtraRequire(event_token)) | |
| 736 | |
| 737 def testScope_partialAliasNoSubtypeRequires(self): | |
| 738 """Tests that partial aliases don't yield subtype requires (regression).""" | |
| 739 input_lines = [ | |
| 740 'goog.provide(\'goog.events.Foo\');', | |
| 741 'goog.scope(function() {', | |
| 742 'goog.events.Foo = {};', | |
| 743 'var Foo = goog.events.Foo;' | |
| 744 'Foo.CssName_ = {};' | |
| 745 'var CssName_ = Foo.CssName_;' | |
| 746 '});' | |
| 747 ] | |
| 748 | |
| 749 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 750 missing_requires, _ = namespaces_info.GetMissingRequires() | |
| 751 self.assertEquals({}, missing_requires) | |
| 752 | |
| 753 def testScope_aliasNamespace(self): | |
| 754 """Tests that an unused alias namespace is not required when available. | |
| 755 | |
| 756 In the example goog.events.Bar is not required, because the namespace | |
| 757 goog.events is already defined because goog.events.Foo is required. | |
| 758 """ | |
| 759 input_lines = [ | |
| 760 'goog.require(\'goog.events.Foo\');', | |
| 761 'goog.scope(function() {', | |
| 762 'var Bar = goog.events.Bar;', | |
| 763 '/** @type {Bar} */;', | |
| 764 'goog.events.Foo;', | |
| 765 '});' | |
| 766 ] | |
| 767 | |
| 768 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 769 missing_requires, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 770 self.assertEquals({}, missing_requires) | |
| 771 self.assertEquals({}, illegal_alias_stmts) | |
| 772 | |
| 773 def testScope_aliasNamespaceIllegal(self): | |
| 774 """Tests that an unused alias namespace is not required when available.""" | |
| 775 input_lines = [ | |
| 776 'goog.scope(function() {', | |
| 777 'var Bar = goog.events.Bar;', | |
| 778 '/** @type {Bar} */;', | |
| 779 '});' | |
| 780 ] | |
| 781 | |
| 782 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 783 missing_requires, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 784 self.assertEquals({}, missing_requires) | |
| 785 self.assertEquals({'goog.events': 2}, _ToLineDict(illegal_alias_stmts)) | |
| 786 | |
| 787 def testScope_provides(self): | |
| 788 """Tests that aliased symbols result in correct provides.""" | |
| 789 input_lines = [ | |
| 790 'goog.scope(function() {', | |
| 791 'goog.bar = {};', | |
| 792 'var bar = goog.bar;', | |
| 793 'bar.Foo = {};', | |
| 794 '});' | |
| 795 ] | |
| 796 | |
| 797 namespaces_info = self._GetNamespacesInfoForScript(input_lines, ['goog']) | |
| 798 missing_provides = namespaces_info.GetMissingProvides() | |
| 799 self.assertEquals({'goog.bar.Foo': 4}, missing_provides) | |
| 800 _, illegal_alias_stmts = namespaces_info.GetMissingRequires() | |
| 801 self.assertEquals({}, illegal_alias_stmts) | |
| 802 | |
| 803 def testSetTestOnlyNamespaces(self): | |
| 804 """Tests that a namespace in setTestOnly makes it a valid provide.""" | |
| 805 namespaces_info = self._GetNamespacesInfoForScript([ | |
| 806 'goog.setTestOnly(\'goog.foo.barTest\');' | |
| 807 ], ['goog']) | |
| 808 | |
| 809 token = self._GetProvideTokens('goog.foo.barTest') | |
| 810 self.assertFalse(namespaces_info.IsExtraProvide(token)) | |
| 811 | |
| 812 token = self._GetProvideTokens('goog.foo.bazTest') | |
| 813 self.assertTrue(namespaces_info.IsExtraProvide(token)) | |
| 814 | |
| 815 def testSetTestOnlyComment(self): | |
| 816 """Ensure a comment in setTestOnly does not cause a created namespace.""" | |
| 817 namespaces_info = self._GetNamespacesInfoForScript([ | |
| 818 'goog.setTestOnly(\'this is a comment\');' | |
| 819 ], ['goog']) | |
| 820 | |
| 821 self.assertEquals( | |
| 822 [], namespaces_info._created_namespaces, | |
| 823 'A comment in setTestOnly should not modify created namespaces.') | |
| 824 | |
| 825 def _GetNamespacesInfoForScript(self, script, closurized_namespaces=None): | |
| 826 _, namespaces_info = self._GetStartTokenAndNamespacesInfoForScript( | |
| 827 script, closurized_namespaces) | |
| 828 | |
| 829 return namespaces_info | |
| 830 | |
| 831 def _GetStartTokenAndNamespacesInfoForScript( | |
| 832 self, script, closurized_namespaces): | |
| 833 | |
| 834 token = testutil.TokenizeSource(script) | |
| 835 return token, self._GetInitializedNamespacesInfo( | |
| 836 token, closurized_namespaces, []) | |
| 837 | |
| 838 def _GetInitializedNamespacesInfo(self, token, closurized_namespaces, | |
| 839 ignored_extra_namespaces): | |
| 840 """Returns a namespaces info initialized with the given token stream.""" | |
| 841 namespaces_info = closurizednamespacesinfo.ClosurizedNamespacesInfo( | |
| 842 closurized_namespaces=closurized_namespaces, | |
| 843 ignored_extra_namespaces=ignored_extra_namespaces) | |
| 844 state_tracker = javascriptstatetracker.JavaScriptStateTracker() | |
| 845 | |
| 846 ecma_pass = ecmametadatapass.EcmaMetaDataPass() | |
| 847 ecma_pass.Process(token) | |
| 848 | |
| 849 state_tracker.DocFlagPass(token, error_handler=None) | |
| 850 | |
| 851 alias_pass = aliaspass.AliasPass(closurized_namespaces) | |
| 852 alias_pass.Process(token) | |
| 853 | |
| 854 while token: | |
| 855 state_tracker.HandleToken(token, state_tracker.GetLastNonSpaceToken()) | |
| 856 namespaces_info.ProcessToken(token, state_tracker) | |
| 857 state_tracker.HandleAfterToken(token) | |
| 858 token = token.next | |
| 859 | |
| 860 return namespaces_info | |
| 861 | |
| 862 def _GetProvideTokens(self, namespace): | |
| 863 """Returns a list of tokens for a goog.require of the given namespace.""" | |
| 864 line_text = 'goog.require(\'' + namespace + '\');\n' | |
| 865 return testutil.TokenizeSource([line_text]) | |
| 866 | |
| 867 def _GetRequireTokens(self, namespace): | |
| 868 """Returns a list of tokens for a goog.require of the given namespace.""" | |
| 869 line_text = 'goog.require(\'' + namespace + '\');\n' | |
| 870 return testutil.TokenizeSource([line_text]) | |
| 871 | |
| 872 if __name__ == '__main__': | |
| 873 googletest.main() | |
| OLD | NEW |