| Index: third_party/closure_linter/closure_linter/scopeutil_test.py
|
| diff --git a/third_party/closure_linter/closure_linter/scopeutil_test.py b/third_party/closure_linter/closure_linter/scopeutil_test.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7a0240009e30b592d493decadeb619cf939f9067
|
| --- /dev/null
|
| +++ b/third_party/closure_linter/closure_linter/scopeutil_test.py
|
| @@ -0,0 +1,210 @@
|
| +#!/usr/bin/env python
|
| +#
|
| +# Copyright 2012 The Closure Linter Authors. All Rights Reserved.
|
| +# Licensed under the Apache License, Version 2.0 (the "License");
|
| +# you may not use this file except in compliance with the License.
|
| +# You may obtain a copy of the License at
|
| +#
|
| +# http://www.apache.org/licenses/LICENSE-2.0
|
| +#
|
| +# Unless required by applicable law or agreed to in writing, software
|
| +# distributed under the License is distributed on an "AS-IS" BASIS,
|
| +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +# See the License for the specific language governing permissions and
|
| +# limitations under the License.
|
| +
|
| +"""Unit tests for the scopeutil module."""
|
| +
|
| +# Allow non-Google copyright
|
| +# pylint: disable=g-bad-file-header
|
| +
|
| +__author__ = ('nnaze@google.com (Nathan Naze)')
|
| +
|
| +
|
| +import unittest as googletest
|
| +
|
| +from closure_linter import ecmametadatapass
|
| +from closure_linter import scopeutil
|
| +from closure_linter import testutil
|
| +
|
| +
|
| +def _FindContexts(start_token):
|
| + """Depth first search of all contexts referenced by a token stream.
|
| +
|
| + Includes contexts' parents, which might not be directly referenced
|
| + by any token in the stream.
|
| +
|
| + Args:
|
| + start_token: First token in the token stream.
|
| +
|
| + Yields:
|
| + All contexts referenced by this token stream.
|
| + """
|
| +
|
| + seen_contexts = set()
|
| +
|
| + # For each token, yield the context if we haven't seen it before.
|
| + for token in start_token:
|
| +
|
| + token_context = token.metadata.context
|
| + contexts = [token_context]
|
| +
|
| + # Also grab all the context's ancestors.
|
| + parent = token_context.parent
|
| + while parent:
|
| + contexts.append(parent)
|
| + parent = parent.parent
|
| +
|
| + # Yield each of these contexts if we've not seen them.
|
| + for context in contexts:
|
| + if context not in seen_contexts:
|
| + yield context
|
| +
|
| + seen_contexts.add(context)
|
| +
|
| +
|
| +def _FindFirstContextOfType(token, context_type):
|
| + """Returns the first statement context."""
|
| + for context in _FindContexts(token):
|
| + if context.type == context_type:
|
| + return context
|
| +
|
| +
|
| +def _WrapWithGoogScope(script):
|
| + """Wraps source code in a goog.scope statement."""
|
| + return 'goog.scope(function() {\n' + script + '\n});'
|
| +
|
| +
|
| +class StatementTest(googletest.TestCase):
|
| +
|
| + def assertAlias(self, expected_match, script):
|
| + start_token = testutil.TokenizeSourceAndRunEcmaPass(script)
|
| + statement = _FindFirstContextOfType(
|
| + start_token, ecmametadatapass.EcmaContext.VAR)
|
| + match = scopeutil.MatchAlias(statement)
|
| + self.assertEquals(expected_match, match)
|
| +
|
| + def testSimpleAliases(self):
|
| + self.assertAlias(
|
| + ('foo', 'goog.foo'),
|
| + _WrapWithGoogScope('var foo = goog.foo;'))
|
| +
|
| + self.assertAlias(
|
| + ('foo', 'goog.foo'),
|
| + _WrapWithGoogScope('var foo = goog.foo')) # No semicolon
|
| +
|
| + def testAliasWithComment(self):
|
| + self.assertAlias(
|
| + ('Component', 'goog.ui.Component'),
|
| + _WrapWithGoogScope('var Component = /* comment */ goog.ui.Component;'))
|
| +
|
| + def testMultilineAlias(self):
|
| + self.assertAlias(
|
| + ('Component', 'goog.ui.Component'),
|
| + _WrapWithGoogScope('var Component = \n goog.ui.\n Component;'))
|
| +
|
| + def testNonSymbolAliasVarStatements(self):
|
| + self.assertAlias(None, _WrapWithGoogScope('var foo = 3;'))
|
| + self.assertAlias(None, _WrapWithGoogScope('var foo = function() {};'))
|
| + self.assertAlias(None, _WrapWithGoogScope('for(var foo = bar;;){}'))
|
| + self.assertAlias(None, _WrapWithGoogScope('var foo = bar ? baz : qux;'))
|
| +
|
| +
|
| +class ScopeBlockTest(googletest.TestCase):
|
| +
|
| + @staticmethod
|
| + def _GetBlocks(source):
|
| + start_token = testutil.TokenizeSourceAndRunEcmaPass(source)
|
| + for context in _FindContexts(start_token):
|
| + if context.type is ecmametadatapass.EcmaContext.BLOCK:
|
| + yield context
|
| +
|
| + def assertNoBlocks(self, script):
|
| + blocks = list(self._GetBlocks(script))
|
| + self.assertEquals([], blocks)
|
| +
|
| + def testNotBlocks(self):
|
| + # Ensure these are not considered blocks.
|
| + self.assertNoBlocks('goog.scope(if{});')
|
| + self.assertNoBlocks('goog.scope(for{});')
|
| + self.assertNoBlocks('goog.scope(switch{});')
|
| + self.assertNoBlocks('goog.scope(function foo{});')
|
| +
|
| + def testNonScopeBlocks(self):
|
| +
|
| + blocks = list(self._GetBlocks('goog.scope(try{});'))
|
| + self.assertEquals(1, len(blocks))
|
| + self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop()))
|
| +
|
| + blocks = list(self._GetBlocks('goog.scope(function(a,b){});'))
|
| + self.assertEquals(1, len(blocks))
|
| + self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop()))
|
| +
|
| + blocks = list(self._GetBlocks('goog.scope(try{} catch(){});'))
|
| + # Two blocks: try and catch.
|
| + self.assertEquals(2, len(blocks))
|
| + self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop()))
|
| + self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop()))
|
| +
|
| + blocks = list(self._GetBlocks('goog.scope(try{} catch(){} finally {});'))
|
| + self.assertEquals(3, len(blocks))
|
| + self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop()))
|
| + self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop()))
|
| + self.assertFalse(scopeutil.IsGoogScopeBlock(blocks.pop()))
|
| +
|
| +
|
| +class AliasTest(googletest.TestCase):
|
| +
|
| + def setUp(self):
|
| + self.start_token = testutil.TokenizeSourceAndRunEcmaPass(_TEST_SCRIPT)
|
| +
|
| + def testMatchAliasStatement(self):
|
| + matches = set()
|
| + for context in _FindContexts(self.start_token):
|
| + match = scopeutil.MatchAlias(context)
|
| + if match:
|
| + matches.add(match)
|
| +
|
| + self.assertEquals(
|
| + set([('bar', 'baz'),
|
| + ('foo', 'this.foo_'),
|
| + ('Component', 'goog.ui.Component'),
|
| + ('MyClass', 'myproject.foo.MyClass'),
|
| + ('NonClosurizedClass', 'aaa.bbb.NonClosurizedClass')]),
|
| + matches)
|
| +
|
| + def testMatchAliasStatement_withClosurizedNamespaces(self):
|
| +
|
| + closurized_namepaces = frozenset(['goog', 'myproject'])
|
| +
|
| + matches = set()
|
| + for context in _FindContexts(self.start_token):
|
| + match = scopeutil.MatchAlias(context)
|
| + if match:
|
| + unused_alias, symbol = match
|
| + if scopeutil.IsInClosurizedNamespace(symbol, closurized_namepaces):
|
| + matches.add(match)
|
| +
|
| + self.assertEquals(
|
| + set([('MyClass', 'myproject.foo.MyClass'),
|
| + ('Component', 'goog.ui.Component')]),
|
| + matches)
|
| +
|
| +_TEST_SCRIPT = """
|
| +goog.scope(function() {
|
| + var Component = goog.ui.Component; // scope alias
|
| + var MyClass = myproject.foo.MyClass; // scope alias
|
| +
|
| + // Scope alias of non-Closurized namespace.
|
| + var NonClosurizedClass = aaa.bbb.NonClosurizedClass;
|
| +
|
| + var foo = this.foo_; // non-scope object property alias
|
| + var bar = baz; // variable alias
|
| +
|
| + var component = new Component();
|
| +});
|
| +
|
| +"""
|
| +
|
| +if __name__ == '__main__':
|
| + googletest.main()
|
|
|