| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # | |
| 3 # Copyright 2012 The Closure Linter Authors. All Rights Reserved. | |
| 4 # Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 # you may not use this file except in compliance with the License. | |
| 6 # You may obtain a copy of the License at | |
| 7 # | |
| 8 # http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 # | |
| 10 # Unless required by applicable law or agreed to in writing, software | |
| 11 # distributed under the License is distributed on an "AS-IS" BASIS, | |
| 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 13 # See the License for the specific language governing permissions and | |
| 14 # limitations under the License. | |
| 15 | |
| 16 """Unit tests for the scopeutil module.""" | |
| 17 | |
| 18 # Allow non-Google copyright | |
| 19 # pylint: disable=g-bad-file-header | |
| 20 | |
| 21 __author__ = ('nnaze@google.com (Nathan Naze)') | |
| 22 | |
| 23 | |
| 24 import unittest as googletest | |
| 25 | |
| 26 from closure_linter import ecmametadatapass | |
| 27 from closure_linter import scopeutil | |
| 28 from closure_linter import testutil | |
| 29 | |
| 30 | |
| 31 def _FindContexts(start_token): | |
| 32 """Depth first search of all contexts referenced by a token stream. | |
| 33 | |
| 34 Includes contexts' parents, which might not be directly referenced | |
| 35 by any token in the stream. | |
| 36 | |
| 37 Args: | |
| 38 start_token: First token in the token stream. | |
| 39 | |
| 40 Yields: | |
| 41 All contexts referenced by this token stream. | |
| 42 """ | |
| 43 | |
| 44 seen_contexts = set() | |
| 45 | |
| 46 # For each token, yield the context if we haven't seen it before. | |
| 47 for token in start_token: | |
| 48 | |
| 49 token_context = token.metadata.context | |
| 50 contexts = [token_context] | |
| 51 | |
| 52 # Also grab all the context's ancestors. | |
| 53 parent = token_context.parent | |
| 54 while parent: | |
| 55 contexts.append(parent) | |
| 56 parent = parent.parent | |
| 57 | |
| 58 # Yield each of these contexts if we've not seen them. | |
| 59 for context in contexts: | |
| 60 if context not in seen_contexts: | |
| 61 yield context | |
| 62 | |
| 63 seen_contexts.add(context) | |
| 64 | |
| 65 | |
| 66 def _FindFirstContextOfType(token, context_type): | |
| 67 """Returns the first statement context.""" | |
| 68 for context in _FindContexts(token): | |
| 69 if context.type == context_type: | |
| 70 return context | |
| 71 | |
| 72 | |
| 73 def _ParseAssignment(script): | |
| 74 start_token = testutil.TokenizeSourceAndRunEcmaPass(script) | |
| 75 statement = _FindFirstContextOfType( | |
| 76 start_token, ecmametadatapass.EcmaContext.VAR) | |
| 77 return statement | |
| 78 | |
| 79 | |
| 80 class StatementTest(googletest.TestCase): | |
| 81 | |
| 82 def assertAlias(self, expected_match, script): | |
| 83 statement = _ParseAssignment(script) | |
| 84 match = scopeutil.MatchAlias(statement) | |
| 85 self.assertEquals(expected_match, match) | |
| 86 | |
| 87 def assertModuleAlias(self, expected_match, script): | |
| 88 statement = _ParseAssignment(script) | |
| 89 match = scopeutil.MatchModuleAlias(statement) | |
| 90 self.assertEquals(expected_match, match) | |
| 91 | |
| 92 def testSimpleAliases(self): | |
| 93 self.assertAlias( | |
| 94 ('foo', 'goog.foo'), | |
| 95 'var foo = goog.foo;') | |
| 96 | |
| 97 self.assertAlias( | |
| 98 ('foo', 'goog.foo'), | |
| 99 'var foo = goog.foo') # No semicolon | |
| 100 | |
| 101 def testAliasWithComment(self): | |
| 102 self.assertAlias( | |
| 103 ('Component', 'goog.ui.Component'), | |
| 104 'var Component = /* comment */ goog.ui.Component;') | |
| 105 | |
| 106 def testMultilineAlias(self): | |
| 107 self.assertAlias( | |
| 108 ('Component', 'goog.ui.Component'), | |
| 109 'var Component = \n goog.ui.\n Component;') | |
| 110 | |
| 111 def testNonSymbolAliasVarStatements(self): | |
| 112 self.assertAlias(None, 'var foo = 3;') | |
| 113 self.assertAlias(None, 'var foo = function() {};') | |
| 114 self.assertAlias(None, 'var foo = bar ? baz : qux;') | |
| 115 | |
| 116 def testModuleAlias(self): | |
| 117 self.assertModuleAlias( | |
| 118 ('foo', 'goog.foo'), | |
| 119 'var foo = goog.require("goog.foo");') | |
| 120 self.assertModuleAlias( | |
| 121 None, | |
| 122 'var foo = goog.require(notastring);') | |
| 123 | |
| 124 | |
| 125 class ScopeBlockTest(googletest.TestCase): | |
| 126 | |
| 127 @staticmethod | |
| 128 def _GetBlocks(source): | |
| 129 start_token = testutil.TokenizeSourceAndRunEcmaPass(source) | |
| 130 for context in _FindContexts(start_token): | |
| 131 if context.type is ecmametadatapass.EcmaContext.BLOCK: | |
| 132 yield context | |
| 133 | |
| 134 def assertNoBlocks(self, script): | |
| 135 blocks = list(self._GetBlocks(script)) | |
| 136 self.assertEquals([], blocks) | |
| 137 | |
| 138 def testNotBlocks(self): | |
| 139 # Ensure these are not considered blocks. | |
| 140 self.assertNoBlocks('goog.scope(if{});') | |
| 141 self.assertNoBlocks('goog.scope(for{});') | |
| 142 self.assertNoBlocks('goog.scope(switch{});') | |
| 143 self.assertNoBlocks('goog.scope(function foo{});') | |
| 144 | |
| 145 def testNonScopeBlocks(self): | |
| 146 | |
| 147 blocks = list(self._GetBlocks('goog.scope(try{});')) | |
| 148 self.assertEquals(1, len(blocks)) | |
| 149 self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop())) | |
| 150 | |
| 151 blocks = list(self._GetBlocks('goog.scope(function(a,b){});')) | |
| 152 self.assertEquals(1, len(blocks)) | |
| 153 self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop())) | |
| 154 | |
| 155 blocks = list(self._GetBlocks('goog.scope(try{} catch(){});')) | |
| 156 # Two blocks: try and catch. | |
| 157 self.assertEquals(2, len(blocks)) | |
| 158 self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop())) | |
| 159 self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop())) | |
| 160 | |
| 161 blocks = list(self._GetBlocks('goog.scope(try{} catch(){} finally {});')) | |
| 162 self.assertEquals(3, len(blocks)) | |
| 163 self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop())) | |
| 164 self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop())) | |
| 165 self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop())) | |
| 166 | |
| 167 | |
| 168 class AliasTest(googletest.TestCase): | |
| 169 | |
| 170 def setUp(self): | |
| 171 self.start_token = testutil.TokenizeSourceAndRunEcmaPass(_TEST_SCRIPT) | |
| 172 | |
| 173 def testMatchAliasStatement(self): | |
| 174 matches = set() | |
| 175 for context in _FindContexts(self.start_token): | |
| 176 match = scopeutil.MatchAlias(context) | |
| 177 if match: | |
| 178 matches.add(match) | |
| 179 | |
| 180 self.assertEquals( | |
| 181 set([('bar', 'baz'), | |
| 182 ('foo', 'this.foo_'), | |
| 183 ('Component', 'goog.ui.Component'), | |
| 184 ('MyClass', 'myproject.foo.MyClass'), | |
| 185 ('NonClosurizedClass', 'aaa.bbb.NonClosurizedClass')]), | |
| 186 matches) | |
| 187 | |
| 188 def testMatchAliasStatement_withClosurizedNamespaces(self): | |
| 189 | |
| 190 closurized_namepaces = frozenset(['goog', 'myproject']) | |
| 191 | |
| 192 matches = set() | |
| 193 for context in _FindContexts(self.start_token): | |
| 194 match = scopeutil.MatchAlias(context) | |
| 195 if match: | |
| 196 unused_alias, symbol = match | |
| 197 if scopeutil.IsInClosurizedNamespace(symbol, closurized_namepaces): | |
| 198 matches.add(match) | |
| 199 | |
| 200 self.assertEquals( | |
| 201 set([('MyClass', 'myproject.foo.MyClass'), | |
| 202 ('Component', 'goog.ui.Component')]), | |
| 203 matches) | |
| 204 | |
| 205 _TEST_SCRIPT = """ | |
| 206 goog.scope(function() { | |
| 207 var Component = goog.ui.Component; // scope alias | |
| 208 var MyClass = myproject.foo.MyClass; // scope alias | |
| 209 | |
| 210 // Scope alias of non-Closurized namespace. | |
| 211 var NonClosurizedClass = aaa.bbb.NonClosurizedClass; | |
| 212 | |
| 213 var foo = this.foo_; // non-scope object property alias | |
| 214 var bar = baz; // variable alias | |
| 215 | |
| 216 var component = new Component(); | |
| 217 }); | |
| 218 | |
| 219 """ | |
| 220 | |
| 221 if __name__ == '__main__': | |
| 222 googletest.main() | |
| OLD | NEW |