/* * Copyright 2007 The Closure Compiler Authors. * * 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. */ package com.google.javascript.jscomp; /** * Tests {@link ConstCheck}. * */ public final class ConstCheckTest extends CompilerTestCase { public ConstCheckTest() { enableNormalize(); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new ConstCheck(compiler); } @Override protected int getNumRepetitions() { return 1; } public void testConstantDefinition1() { testSame("var XYZ = 1;"); } public void testConstantDefinition2() { testSame("var a$b$XYZ = 1;"); } public void testConstantInitializedInAnonymousNamespace1() { testSame("var XYZ; (function(){ XYZ = 1; })();"); } public void testConstantInitializedInAnonymousNamespace2() { testSame("var a$b$XYZ; (function(){ a$b$XYZ = 1; })();"); } public void testObjectModified() { testSame("var IE = true, XYZ = {a:1,b:1}; if (IE) XYZ['c'] = 1;"); } public void testObjectPropertyInitializedLate() { testSame("var XYZ = {}; for (var i = 0; i < 10; i++) { XYZ[i] = i; }"); } public void testObjectRedefined1() { testError("var XYZ = {}; XYZ = 2;"); } public void testConstantRedefined1() { testError("var XYZ = 1; XYZ = 2;"); } public void testConstantRedefined2() { testError("var a$b$XYZ = 1; a$b$XYZ = 2;"); } public void testConstantRedefinedInLocalScope1() { testError("var XYZ = 1; (function(){ XYZ = 2; })();"); } public void testConstantRedefinedInLocalScope2() { testError("var a$b$XYZ = 1; (function(){ a$b$XYZ = 2; })();"); } public void testConstantRedefinedInLocalScopeOutOfOrder() { testError("function f() { XYZ = 2; } var XYZ = 1;"); } public void testConstantPostIncremented1() { testError("var XYZ = 1; XYZ++;"); } public void testConstantPostIncremented2() { testError("var a$b$XYZ = 1; a$b$XYZ++;"); } public void testConstantPreIncremented1() { testError("var XYZ = 1; ++XYZ;"); } public void testConstantPreIncremented2() { testError("var a$b$XYZ = 1; ++a$b$XYZ;"); } public void testConstantPostDecremented1() { testError("var XYZ = 1; XYZ--;"); } public void testConstantPostDecremented2() { testError("var a$b$XYZ = 1; a$b$XYZ--;"); } public void testConstantPreDecremented1() { testError("var XYZ = 1; --XYZ;"); } public void testConstantPreDecremented2() { testError("var a$b$XYZ = 1; --a$b$XYZ;"); } public void testAbbreviatedArithmeticAssignment1() { testError("var XYZ = 1; XYZ += 2;"); } public void testAbbreviatedArithmeticAssignment2() { testError("var a$b$XYZ = 1; a$b$XYZ %= 2;"); } public void testAbbreviatedBitAssignment1() { testError("var XYZ = 1; XYZ |= 2;"); } public void testAbbreviatedBitAssignment2() { testError("var a$b$XYZ = 1; a$b$XYZ &= 2;"); } public void testAbbreviatedShiftAssignment1() { testError("var XYZ = 1; XYZ >>= 2;"); } public void testAbbreviatedShiftAssignment2() { testError("var a$b$XYZ = 1; a$b$XYZ <<= 2;"); } public void testConstAnnotation() { testError("/** @const */ var xyz = 1; xyz = 3;"); } public void testConstSuppressionInFileJsDoc() { testSame("/**\n" + " * @fileoverview\n" + " * @suppress {const}\n" + " */\n" + "/** @const */ var xyz = 1; xyz = 3;"); } public void testConstSuppressionOnAssignment() { testSame("/** @const */ var xyz = 1; /** @suppress {const} */ xyz = 3;"); } public void testConstSuppressionOnAddAssign() { testSame("/** @const */ var xyz = 1; /** @suppress {const} */ xyz += 1;"); } // If there are two 'var' statements for the same variable, and both are in the JS // (not in externs), the second will be normalized to an assignment, and the // JSDoc with the suppression will be on the new node. public void testConstSuppressionOnVar() { String before = "/** @const */ var xyz = 1;\n/** @suppress {const} */ var xyz = 3;"; String after = "/** @const */ var xyz = 1;\n/** @suppress {const} */ xyz = 3;"; test(before, after); } // If there are two 'var' statements for the same variable, one in externs and // one in the JS, there is no normalization, and the suppression remains on the // statement in the JS. public void testConstSuppressionOnVarFromExterns() { String externs = "/** @const */ var xyz;"; String js = "/** @suppress {const} */ var xyz = 3;"; test(externs, js, js, null, null); } public void testConstSuppressionOnInc() { testSame("/** @const */ var xyz = 1; /** @suppress {const} */ xyz++;"); } public void testConstNameInExterns() { String externs = "/** @const */ var FOO;"; String js = "FOO = 1;"; test(externs, js, (String) null, null, ConstCheck.CONST_REASSIGNED_VALUE_ERROR); } private void testError(String js) { testWarning(js, ConstCheck.CONST_REASSIGNED_VALUE_ERROR); } }