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 javascriptstatetracker module.""" 17 18# Allow non-Google copyright 19# pylint: disable=g-bad-file-header 20 21__author__ = ('nnaze@google.com (Nathan Naze)') 22 23 24import unittest as googletest 25 26from closure_linter import javascripttokens 27from closure_linter import testutil 28from closure_linter import tokenutil 29 30 31_FUNCTION_SCRIPT = """\ 32var a = 3; 33 34function foo(aaa, bbb, ccc) { 35 var b = 4; 36} 37 38 39/** 40 * JSDoc comment. 41 */ 42var bar = function(ddd, eee, fff) { 43 44}; 45 46 47/** 48 * Verify that nested functions get their proper parameters recorded. 49 */ 50var baz = function(ggg, hhh, iii) { 51 var qux = function(jjj, kkk, lll) { 52 }; 53 // make sure that entering a new block does not change baz' parameters. 54 {}; 55}; 56 57""" 58 59 60class FunctionTest(googletest.TestCase): 61 62 def testFunctionParse(self): 63 functions, _ = testutil.ParseFunctionsAndComments(_FUNCTION_SCRIPT) 64 self.assertEquals(4, len(functions)) 65 66 # First function 67 function = functions[0] 68 self.assertEquals(['aaa', 'bbb', 'ccc'], function.parameters) 69 70 start_token = function.start_token 71 end_token = function.end_token 72 73 self.assertEquals( 74 javascripttokens.JavaScriptTokenType.FUNCTION_DECLARATION, 75 function.start_token.type) 76 77 self.assertEquals('function', start_token.string) 78 self.assertEquals(3, start_token.line_number) 79 self.assertEquals(0, start_token.start_index) 80 81 self.assertEquals('}', end_token.string) 82 self.assertEquals(5, end_token.line_number) 83 self.assertEquals(0, end_token.start_index) 84 85 self.assertEquals('foo', function.name) 86 87 self.assertIsNone(function.doc) 88 89 # Second function 90 function = functions[1] 91 self.assertEquals(['ddd', 'eee', 'fff'], function.parameters) 92 93 start_token = function.start_token 94 end_token = function.end_token 95 96 self.assertEquals( 97 javascripttokens.JavaScriptTokenType.FUNCTION_DECLARATION, 98 function.start_token.type) 99 100 self.assertEquals('function', start_token.string) 101 self.assertEquals(11, start_token.line_number) 102 self.assertEquals(10, start_token.start_index) 103 104 self.assertEquals('}', end_token.string) 105 self.assertEquals(13, end_token.line_number) 106 self.assertEquals(0, end_token.start_index) 107 108 self.assertEquals('bar', function.name) 109 110 self.assertIsNotNone(function.doc) 111 112 # Check function JSDoc 113 doc = function.doc 114 doc_tokens = tokenutil.GetTokenRange(doc.start_token, doc.end_token) 115 116 comment_type = javascripttokens.JavaScriptTokenType.COMMENT 117 comment_tokens = filter(lambda t: t.type is comment_type, doc_tokens) 118 119 self.assertEquals('JSDoc comment.', 120 tokenutil.TokensToString(comment_tokens).strip()) 121 122 # Third function 123 function = functions[2] 124 self.assertEquals(['ggg', 'hhh', 'iii'], function.parameters) 125 126 start_token = function.start_token 127 end_token = function.end_token 128 129 self.assertEquals( 130 javascripttokens.JavaScriptTokenType.FUNCTION_DECLARATION, 131 function.start_token.type) 132 133 self.assertEquals('function', start_token.string) 134 self.assertEquals(19, start_token.line_number) 135 self.assertEquals(10, start_token.start_index) 136 137 self.assertEquals('}', end_token.string) 138 self.assertEquals(24, end_token.line_number) 139 self.assertEquals(0, end_token.start_index) 140 141 self.assertEquals('baz', function.name) 142 self.assertIsNotNone(function.doc) 143 144 # Fourth function (inside third function) 145 function = functions[3] 146 self.assertEquals(['jjj', 'kkk', 'lll'], function.parameters) 147 148 start_token = function.start_token 149 end_token = function.end_token 150 151 self.assertEquals( 152 javascripttokens.JavaScriptTokenType.FUNCTION_DECLARATION, 153 function.start_token.type) 154 155 self.assertEquals('function', start_token.string) 156 self.assertEquals(20, start_token.line_number) 157 self.assertEquals(12, start_token.start_index) 158 159 self.assertEquals('}', end_token.string) 160 self.assertEquals(21, end_token.line_number) 161 self.assertEquals(2, end_token.start_index) 162 163 self.assertEquals('qux', function.name) 164 self.assertIsNone(function.doc) 165 166 167 168class CommentTest(googletest.TestCase): 169 170 def testGetDescription(self): 171 comment = self._ParseComment(""" 172 /** 173 * Comment targeting goog.foo. 174 * 175 * This is the second line. 176 * @param {number} foo The count of foo. 177 */ 178 target;""") 179 180 self.assertEqual( 181 'Comment targeting goog.foo.\n\nThis is the second line.', 182 comment.description) 183 184 def testCommentGetTarget(self): 185 self.assertCommentTarget('goog.foo', """ 186 /** 187 * Comment targeting goog.foo. 188 */ 189 goog.foo = 6; 190 """) 191 192 self.assertCommentTarget('bar', """ 193 /** 194 * Comment targeting bar. 195 */ 196 var bar = "Karate!"; 197 """) 198 199 self.assertCommentTarget('doThing', """ 200 /** 201 * Comment targeting doThing. 202 */ 203 function doThing() {}; 204 """) 205 206 self.assertCommentTarget('this.targetProperty', """ 207 goog.bar.Baz = function() { 208 /** 209 * Comment targeting targetProperty. 210 */ 211 this.targetProperty = 3; 212 }; 213 """) 214 215 self.assertCommentTarget('goog.bar.prop', """ 216 /** 217 * Comment targeting goog.bar.prop. 218 */ 219 goog.bar.prop; 220 """) 221 222 self.assertCommentTarget('goog.aaa.bbb', """ 223 /** 224 * Comment targeting goog.aaa.bbb. 225 */ 226 (goog.aaa.bbb) 227 """) 228 229 self.assertCommentTarget('theTarget', """ 230 /** 231 * Comment targeting symbol preceded by newlines, whitespace, 232 * and parens -- things we ignore. 233 */ 234 (theTarget) 235 """) 236 237 self.assertCommentTarget(None, """ 238 /** 239 * @fileoverview File overview. 240 */ 241 (notATarget) 242 """) 243 244 self.assertCommentTarget(None, """ 245 /** 246 * Comment that doesn't find a target. 247 */ 248 """) 249 250 self.assertCommentTarget('theTarget.is.split.across.lines', """ 251 /** 252 * Comment that addresses a symbol split across lines. 253 */ 254 (theTarget.is.split 255 .across.lines) 256 """) 257 258 self.assertCommentTarget('theTarget.is.split.across.lines', """ 259 /** 260 * Comment that addresses a symbol split across lines. 261 */ 262 (theTarget.is.split. 263 across.lines) 264 """) 265 266 def _ParseComment(self, script): 267 """Parse a script that contains one comment and return it.""" 268 _, comments = testutil.ParseFunctionsAndComments(script) 269 self.assertEquals(1, len(comments)) 270 return comments[0] 271 272 def assertCommentTarget(self, target, script): 273 comment = self._ParseComment(script) 274 self.assertEquals(target, comment.GetTargetIdentifier()) 275 276 277if __name__ == '__main__': 278 googletest.main() 279