860 lines
29 KiB
Java
860 lines
29 KiB
Java
|
/*
|
||
|
* Copyright 2006 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;
|
||
|
|
||
|
import com.google.javascript.rhino.Node;
|
||
|
import com.google.javascript.jscomp.CheckLevel;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.BASE_CLASS_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.DUPLICATE_NAMESPACE_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.EXPECTED_OBJECTLIT_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.FUNCTION_NAMESPACE_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.INVALID_ARGUMENT_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.INVALID_PROVIDE_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.INVALID_STYLE_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.LATE_PROVIDE_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.MISSING_PROVIDE_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.NULL_ARGUMENT_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.TOO_MANY_ARGUMENTS_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.XMODULE_REQUIRE_ERROR;
|
||
|
import static com.google.javascript.jscomp.ProcessClosurePrimitives.INVALID_CSS_RENAMING_MAP;
|
||
|
|
||
|
/**
|
||
|
* Tests for {@link ProcessClosurePrimitives}.
|
||
|
*
|
||
|
*/
|
||
|
public class ProcessClosurePrimitivesTest extends CompilerTestCase {
|
||
|
private String additionalCode;
|
||
|
private String additionalEndCode;
|
||
|
private boolean addAdditionalNamespace;
|
||
|
|
||
|
public ProcessClosurePrimitivesTest() {
|
||
|
enableLineNumberCheck(true);
|
||
|
}
|
||
|
|
||
|
@Override protected void setUp() {
|
||
|
additionalCode = null;
|
||
|
additionalEndCode = null;
|
||
|
addAdditionalNamespace = false;
|
||
|
}
|
||
|
|
||
|
@Override public CompilerPass getProcessor(final Compiler compiler) {
|
||
|
if ((additionalCode == null) && (additionalEndCode == null)) {
|
||
|
return new ProcessClosurePrimitives(
|
||
|
compiler, null, CheckLevel.ERROR);
|
||
|
} else {
|
||
|
return new CompilerPass() {
|
||
|
@Override
|
||
|
public void process(Node externs, Node root) {
|
||
|
// Process the original code.
|
||
|
new ProcessClosurePrimitives(compiler, null, CheckLevel.OFF)
|
||
|
.process(externs, root);
|
||
|
|
||
|
// Inject additional code at the beginning.
|
||
|
if (additionalCode != null) {
|
||
|
SourceFile file =
|
||
|
SourceFile.fromCode("additionalcode", additionalCode);
|
||
|
Node scriptNode = root.getFirstChild();
|
||
|
Node newScriptNode = new CompilerInput(file).getAstRoot(compiler);
|
||
|
if (addAdditionalNamespace) {
|
||
|
newScriptNode.getFirstChild()
|
||
|
.putBooleanProp(Node.IS_NAMESPACE, true);
|
||
|
}
|
||
|
while (newScriptNode.getLastChild() != null) {
|
||
|
Node lastChild = newScriptNode.getLastChild();
|
||
|
newScriptNode.removeChild(lastChild);
|
||
|
scriptNode.addChildBefore(lastChild, scriptNode.getFirstChild());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Inject additional code at the end.
|
||
|
if (additionalEndCode != null) {
|
||
|
SourceFile file =
|
||
|
SourceFile.fromCode("additionalendcode", additionalEndCode);
|
||
|
Node scriptNode = root.getFirstChild();
|
||
|
Node newScriptNode = new CompilerInput(file).getAstRoot(compiler);
|
||
|
if (addAdditionalNamespace) {
|
||
|
newScriptNode.getFirstChild()
|
||
|
.putBooleanProp(Node.IS_NAMESPACE, true);
|
||
|
}
|
||
|
while (newScriptNode.getFirstChild() != null) {
|
||
|
Node firstChild = newScriptNode.getFirstChild();
|
||
|
newScriptNode.removeChild(firstChild);
|
||
|
scriptNode.addChildToBack(firstChild);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Process the tree a second time.
|
||
|
new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR)
|
||
|
.process(externs, root);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override public int getNumRepetitions() {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
public void testSimpleProvides() {
|
||
|
test("goog.provide('foo');",
|
||
|
"var foo={};");
|
||
|
test("goog.provide('foo.bar');",
|
||
|
"var foo={}; foo.bar={};");
|
||
|
test("goog.provide('foo.bar.baz');",
|
||
|
"var foo={}; foo.bar={}; foo.bar.baz={};");
|
||
|
test("goog.provide('foo.bar.baz.boo');",
|
||
|
"var foo={}; foo.bar={}; foo.bar.baz={}; foo.bar.baz.boo={};");
|
||
|
test("goog.provide('goog.bar');",
|
||
|
"goog.bar={};"); // goog is special-cased
|
||
|
}
|
||
|
|
||
|
public void testMultipleProvides() {
|
||
|
test("goog.provide('foo.bar'); goog.provide('foo.baz');",
|
||
|
"var foo={}; foo.bar={}; foo.baz={};");
|
||
|
test("goog.provide('foo.bar.baz'); goog.provide('foo.boo.foo');",
|
||
|
"var foo={}; foo.bar={}; foo.bar.baz={}; foo.boo={}; foo.boo.foo={};");
|
||
|
test("goog.provide('foo.bar.baz'); goog.provide('foo.bar.boo');",
|
||
|
"var foo={}; foo.bar={}; foo.bar.baz={}; foo.bar.boo={};");
|
||
|
test("goog.provide('foo.bar.baz'); goog.provide('goog.bar.boo');",
|
||
|
"var foo={}; foo.bar={}; foo.bar.baz={}; goog.bar={}; " +
|
||
|
"goog.bar.boo={};");
|
||
|
}
|
||
|
|
||
|
public void testRemovalOfProvidedObjLit() {
|
||
|
test("goog.provide('foo'); foo = 0;",
|
||
|
"var foo = 0;");
|
||
|
test("goog.provide('foo'); foo = {a: 0};",
|
||
|
"var foo = {a: 0};");
|
||
|
test("goog.provide('foo'); foo = function(){};",
|
||
|
"var foo = function(){};");
|
||
|
test("goog.provide('foo'); var foo = 0;",
|
||
|
"var foo = 0;");
|
||
|
test("goog.provide('foo'); var foo = {a: 0};",
|
||
|
"var foo = {a: 0};");
|
||
|
test("goog.provide('foo'); var foo = function(){};",
|
||
|
"var foo = function(){};");
|
||
|
test("goog.provide('foo.bar.Baz'); foo.bar.Baz=function(){};",
|
||
|
"var foo={}; foo.bar={}; foo.bar.Baz=function(){};");
|
||
|
test("goog.provide('foo.bar.moo'); foo.bar.moo={E:1,S:2};",
|
||
|
"var foo={}; foo.bar={}; foo.bar.moo={E:1,S:2};");
|
||
|
test("goog.provide('foo.bar.moo'); foo.bar.moo={E:1}; foo.bar.moo={E:2};",
|
||
|
"var foo={}; foo.bar={}; foo.bar.moo={E:1}; foo.bar.moo={E:2};");
|
||
|
}
|
||
|
|
||
|
public void testProvidedDeclaredFunctionError() {
|
||
|
test("goog.provide('foo'); function foo(){}",
|
||
|
null, FUNCTION_NAMESPACE_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testRemovalMultipleAssignment1() {
|
||
|
test("goog.provide('foo'); foo = 0; foo = 1",
|
||
|
"var foo = 0; foo = 1;");
|
||
|
}
|
||
|
|
||
|
public void testRemovalMultipleAssignment2() {
|
||
|
test("goog.provide('foo'); var foo = 0; foo = 1",
|
||
|
"var foo = 0; foo = 1;");
|
||
|
}
|
||
|
|
||
|
public void testRemovalMultipleAssignment3() {
|
||
|
test("goog.provide('foo'); foo = 0; var foo = 1",
|
||
|
"foo = 0; var foo = 1;");
|
||
|
}
|
||
|
|
||
|
public void testRemovalMultipleAssignment4() {
|
||
|
test("goog.provide('foo.bar'); foo.bar = 0; foo.bar = 1",
|
||
|
"var foo = {}; foo.bar = 0; foo.bar = 1");
|
||
|
}
|
||
|
|
||
|
public void testNoRemovalFunction1() {
|
||
|
test("goog.provide('foo'); function f(){foo = 0}",
|
||
|
"var foo = {}; function f(){foo = 0}");
|
||
|
}
|
||
|
|
||
|
public void testNoRemovalFunction2() {
|
||
|
test("goog.provide('foo'); function f(){var foo = 0}",
|
||
|
"var foo = {}; function f(){var foo = 0}");
|
||
|
}
|
||
|
|
||
|
public void testRemovalMultipleAssignmentInIf1() {
|
||
|
test("goog.provide('foo'); if (true) { var foo = 0 } else { foo = 1 }",
|
||
|
"if (true) { var foo = 0 } else { foo = 1 }");
|
||
|
}
|
||
|
|
||
|
public void testRemovalMultipleAssignmentInIf2() {
|
||
|
test("goog.provide('foo'); if (true) { foo = 0 } else { var foo = 1 }",
|
||
|
"if (true) { foo = 0 } else { var foo = 1 }");
|
||
|
}
|
||
|
|
||
|
public void testRemovalMultipleAssignmentInIf3() {
|
||
|
test("goog.provide('foo'); if (true) { foo = 0 } else { foo = 1 }",
|
||
|
"if (true) { var foo = 0 } else { foo = 1 }");
|
||
|
}
|
||
|
|
||
|
public void testRemovalMultipleAssignmentInIf4() {
|
||
|
test("goog.provide('foo.bar');" +
|
||
|
"if (true) { foo.bar = 0 } else { foo.bar = 1 }",
|
||
|
"var foo = {}; if (true) { foo.bar = 0 } else { foo.bar = 1 }");
|
||
|
}
|
||
|
|
||
|
public void testMultipleDeclarationError1() {
|
||
|
String rest = "if (true) { foo.bar = 0 } else { foo.bar = 1 }";
|
||
|
test("goog.provide('foo.bar');" + "var foo = {};" + rest,
|
||
|
"var foo = {};" + "var foo = {};" + rest);
|
||
|
}
|
||
|
|
||
|
public void testMultipleDeclarationError2() {
|
||
|
test("goog.provide('foo.bar');" +
|
||
|
"if (true) { var foo = {}; foo.bar = 0 } else { foo.bar = 1 }",
|
||
|
"var foo = {};" +
|
||
|
"if (true) {" +
|
||
|
" var foo = {}; foo.bar = 0" +
|
||
|
"} else {" +
|
||
|
" foo.bar = 1" +
|
||
|
"}");
|
||
|
}
|
||
|
|
||
|
public void testMultipleDeclarationError3() {
|
||
|
test("goog.provide('foo.bar');" +
|
||
|
"if (true) { foo.bar = 0 } else { var foo = {}; foo.bar = 1 }",
|
||
|
"var foo = {};" +
|
||
|
"if (true) {" +
|
||
|
" foo.bar = 0" +
|
||
|
"} else {" +
|
||
|
" var foo = {}; foo.bar = 1" +
|
||
|
"}");
|
||
|
}
|
||
|
|
||
|
public void testProvideAfterDeclarationError() {
|
||
|
test("var x = 42; goog.provide('x');",
|
||
|
"var x = 42; var x = {}");
|
||
|
}
|
||
|
|
||
|
public void testProvideErrorCases() {
|
||
|
test("goog.provide();", "", NULL_ARGUMENT_ERROR);
|
||
|
test("goog.provide(5);", "", INVALID_ARGUMENT_ERROR);
|
||
|
test("goog.provide([]);", "", INVALID_ARGUMENT_ERROR);
|
||
|
test("goog.provide({});", "", INVALID_ARGUMENT_ERROR);
|
||
|
test("goog.provide('foo', 'bar');", "", TOO_MANY_ARGUMENTS_ERROR);
|
||
|
test("goog.provide('foo'); goog.provide('foo');", "",
|
||
|
DUPLICATE_NAMESPACE_ERROR);
|
||
|
test("goog.provide('foo.bar'); goog.provide('foo'); goog.provide('foo');",
|
||
|
"", DUPLICATE_NAMESPACE_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testRemovalOfRequires() {
|
||
|
test("goog.provide('foo'); goog.require('foo');",
|
||
|
"var foo={};");
|
||
|
test("goog.provide('foo.bar'); goog.require('foo.bar');",
|
||
|
"var foo={}; foo.bar={};");
|
||
|
test("goog.provide('foo.bar.baz'); goog.require('foo.bar.baz');",
|
||
|
"var foo={}; foo.bar={}; foo.bar.baz={};");
|
||
|
test("goog.provide('foo'); var x = 3; goog.require('foo'); something();",
|
||
|
"var foo={}; var x = 3; something();");
|
||
|
testSame("foo.require('foo.bar');");
|
||
|
}
|
||
|
|
||
|
public void testRequireErrorCases() {
|
||
|
test("goog.require();", "", NULL_ARGUMENT_ERROR);
|
||
|
test("goog.require(5);", "", INVALID_ARGUMENT_ERROR);
|
||
|
test("goog.require([]);", "", INVALID_ARGUMENT_ERROR);
|
||
|
test("goog.require({});", "", INVALID_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testLateProvides() {
|
||
|
test("goog.require('foo'); goog.provide('foo');",
|
||
|
"var foo={};", LATE_PROVIDE_ERROR);
|
||
|
test("goog.require('foo.bar'); goog.provide('foo.bar');",
|
||
|
"var foo={}; foo.bar={};", LATE_PROVIDE_ERROR);
|
||
|
test("goog.provide('foo.bar'); goog.require('foo'); goog.provide('foo');",
|
||
|
"var foo={}; foo.bar={};", LATE_PROVIDE_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testMissingProvides() {
|
||
|
test("goog.require('foo');",
|
||
|
"", MISSING_PROVIDE_ERROR);
|
||
|
test("goog.provide('foo'); goog.require('Foo');",
|
||
|
"var foo={};", MISSING_PROVIDE_ERROR);
|
||
|
test("goog.provide('foo'); goog.require('foo.bar');",
|
||
|
"var foo={};", MISSING_PROVIDE_ERROR);
|
||
|
test("goog.provide('foo'); var EXPERIMENT_FOO = true; " +
|
||
|
"if (EXPERIMENT_FOO) {goog.require('foo.bar');}",
|
||
|
"var foo={}; var EXPERIMENT_FOO = true; if (EXPERIMENT_FOO) {}",
|
||
|
MISSING_PROVIDE_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testAddDependency() {
|
||
|
test("goog.addDependency('x.js', ['A', 'B'], []);", "0");
|
||
|
|
||
|
Compiler compiler = getLastCompiler();
|
||
|
assertTrue(compiler.getTypeRegistry().isForwardDeclaredType("A"));
|
||
|
assertTrue(compiler.getTypeRegistry().isForwardDeclaredType("B"));
|
||
|
assertFalse(compiler.getTypeRegistry().isForwardDeclaredType("C"));
|
||
|
}
|
||
|
|
||
|
public void testValidSetCssNameMapping() {
|
||
|
test("goog.setCssNameMapping({foo:'bar',\"biz\":'baz'});", "");
|
||
|
CssRenamingMap map = getLastCompiler().getCssRenamingMap();
|
||
|
assertNotNull(map);
|
||
|
assertEquals("bar", map.get("foo"));
|
||
|
assertEquals("baz", map.get("biz"));
|
||
|
}
|
||
|
|
||
|
public void testValidSetCssNameMappingWithType() {
|
||
|
test("goog.setCssNameMapping({foo:'bar',\"biz\":'baz'}, 'BY_PART');", "");
|
||
|
CssRenamingMap map = getLastCompiler().getCssRenamingMap();
|
||
|
assertNotNull(map);
|
||
|
assertEquals("bar", map.get("foo"));
|
||
|
assertEquals("baz", map.get("biz"));
|
||
|
|
||
|
test("goog.setCssNameMapping({foo:'bar',biz:'baz','biz-foo':'baz-bar'}," +
|
||
|
" 'BY_WHOLE');", "");
|
||
|
map = getLastCompiler().getCssRenamingMap();
|
||
|
assertNotNull(map);
|
||
|
assertEquals("bar", map.get("foo"));
|
||
|
assertEquals("baz", map.get("biz"));
|
||
|
assertEquals("baz-bar", map.get("biz-foo"));
|
||
|
}
|
||
|
|
||
|
public void testSetCssNameMappingNonStringValueReturnsError() {
|
||
|
// Make sure the argument is an object literal.
|
||
|
test("var BAR = {foo:'bar'}; goog.setCssNameMapping(BAR);", "",
|
||
|
EXPECTED_OBJECTLIT_ERROR);
|
||
|
test("goog.setCssNameMapping([]);", "",
|
||
|
EXPECTED_OBJECTLIT_ERROR);
|
||
|
test("goog.setCssNameMapping(false);", "",
|
||
|
EXPECTED_OBJECTLIT_ERROR);
|
||
|
test("goog.setCssNameMapping(null);", "",
|
||
|
EXPECTED_OBJECTLIT_ERROR);
|
||
|
test("goog.setCssNameMapping(undefined);", "",
|
||
|
EXPECTED_OBJECTLIT_ERROR);
|
||
|
|
||
|
// Make sure all values of the object literal are string literals.
|
||
|
test("var BAR = 'bar'; goog.setCssNameMapping({foo:BAR});", "",
|
||
|
NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR);
|
||
|
test("goog.setCssNameMapping({foo:6});", "",
|
||
|
NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR);
|
||
|
test("goog.setCssNameMapping({foo:false});", "",
|
||
|
NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR);
|
||
|
test("goog.setCssNameMapping({foo:null});", "",
|
||
|
NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR);
|
||
|
test("goog.setCssNameMapping({foo:undefined});", "",
|
||
|
NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testSetCssNameMappingValidity() {
|
||
|
// Make sure that the keys don't have -'s
|
||
|
test("goog.setCssNameMapping({'a': 'b', 'a-a': 'c'})", "", null,
|
||
|
INVALID_CSS_RENAMING_MAP);
|
||
|
|
||
|
// In full mode, we check that map(a-b)=map(a)-map(b)
|
||
|
test("goog.setCssNameMapping({'a': 'b', 'a-a': 'c'}, 'BY_WHOLE')", "", null,
|
||
|
INVALID_CSS_RENAMING_MAP);
|
||
|
|
||
|
// Unknown mapping type
|
||
|
test("goog.setCssNameMapping({foo:'bar'}, 'UNKNOWN');", "",
|
||
|
INVALID_STYLE_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testBadCrossModuleRequire() {
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"",
|
||
|
"goog.provide('goog.ui');",
|
||
|
"goog.require('goog.ui');"),
|
||
|
new String[] {
|
||
|
"",
|
||
|
"goog.ui = {};",
|
||
|
""
|
||
|
},
|
||
|
null,
|
||
|
XMODULE_REQUIRE_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testGoodCrossModuleRequire1() {
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"goog.provide('goog.ui');",
|
||
|
"",
|
||
|
"goog.require('goog.ui');"),
|
||
|
new String[] {
|
||
|
"goog.ui = {};",
|
||
|
"",
|
||
|
"",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void testGoodCrossModuleRequire2() {
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"",
|
||
|
"",
|
||
|
"goog.provide('goog.ui'); goog.require('goog.ui');"),
|
||
|
new String[] {
|
||
|
"",
|
||
|
"",
|
||
|
"goog.ui = {};",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Tests providing additional code with non-overlapping var namespace.
|
||
|
public void testSimpleAdditionalProvide() {
|
||
|
additionalCode = "goog.provide('b.B'); b.B = {};";
|
||
|
test("goog.provide('a.A'); a.A = {};",
|
||
|
"var b={};b.B={};var a={};a.A={};");
|
||
|
}
|
||
|
|
||
|
// Same as above, but with the additional code added after the original.
|
||
|
public void testSimpleAdditionalProvideAtEnd() {
|
||
|
additionalEndCode = "goog.provide('b.B'); b.B = {};";
|
||
|
test("goog.provide('a.A'); a.A = {};",
|
||
|
"var a={};a.A={};var b={};b.B={};");
|
||
|
}
|
||
|
|
||
|
// Tests providing additional code with non-overlapping dotted namespace.
|
||
|
public void testSimpleDottedAdditionalProvide() {
|
||
|
additionalCode = "goog.provide('a.b.B'); a.b.B = {};";
|
||
|
test("goog.provide('c.d.D'); c.d.D = {};",
|
||
|
"var a={};a.b={};a.b.B={};var c={};c.d={};c.d.D={};");
|
||
|
}
|
||
|
|
||
|
// Tests providing additional code with overlapping var namespace.
|
||
|
public void testOverlappingAdditionalProvide() {
|
||
|
additionalCode = "goog.provide('a.B'); a.B = {};";
|
||
|
test("goog.provide('a.A'); a.A = {};",
|
||
|
"var a={};a.B={};a.A={};");
|
||
|
}
|
||
|
|
||
|
// Tests providing additional code with overlapping var namespace.
|
||
|
public void testOverlappingAdditionalProvideAtEnd() {
|
||
|
additionalEndCode = "goog.provide('a.B'); a.B = {};";
|
||
|
test("goog.provide('a.A'); a.A = {};",
|
||
|
"var a={};a.A={};a.B={};");
|
||
|
}
|
||
|
|
||
|
// Tests providing additional code with overlapping dotted namespace.
|
||
|
public void testOverlappingDottedAdditionalProvide() {
|
||
|
additionalCode = "goog.provide('a.b.B'); a.b.B = {};";
|
||
|
test("goog.provide('a.b.C'); a.b.C = {};",
|
||
|
"var a={};a.b={};a.b.B={};a.b.C={};");
|
||
|
}
|
||
|
|
||
|
// Tests that a require of additional code generates no error.
|
||
|
public void testRequireOfAdditionalProvide() {
|
||
|
additionalCode = "goog.provide('b.B'); b.B = {};";
|
||
|
test("goog.require('b.B'); goog.provide('a.A'); a.A = {};",
|
||
|
"var b={};b.B={};var a={};a.A={};");
|
||
|
}
|
||
|
|
||
|
// Tests that a require not in additional code generates (only) one error.
|
||
|
public void testMissingRequireWithAdditionalProvide() {
|
||
|
additionalCode = "goog.provide('b.B'); b.B = {};";
|
||
|
test("goog.require('b.C'); goog.provide('a.A'); a.A = {};",
|
||
|
"var b={};b.B={};var a={};a.A={};",
|
||
|
MISSING_PROVIDE_ERROR);
|
||
|
}
|
||
|
|
||
|
// Tests that a require in additional code generates no error.
|
||
|
public void testLateRequire() {
|
||
|
additionalEndCode = "goog.require('a.A');";
|
||
|
test("goog.provide('a.A'); a.A = {};",
|
||
|
"var a={};a.A={};");
|
||
|
}
|
||
|
|
||
|
// Tests a case where code is reordered after processing provides and then
|
||
|
// provides are processed again.
|
||
|
public void testReorderedProvides() {
|
||
|
additionalCode = "a.B = {};"; // as if a.B was after a.A originally
|
||
|
addAdditionalNamespace = true;
|
||
|
test("goog.provide('a.A'); a.A = {};",
|
||
|
"var a={};a.B={};a.A={};");
|
||
|
}
|
||
|
|
||
|
// Another version of above.
|
||
|
public void testReorderedProvides2() {
|
||
|
additionalEndCode = "a.B = {};";
|
||
|
addAdditionalNamespace = true;
|
||
|
test("goog.provide('a.A'); a.A = {};",
|
||
|
"var a={};a.A={};a.B={};");
|
||
|
}
|
||
|
|
||
|
// Provide a name before the definition of the class providing the
|
||
|
// parent namespace.
|
||
|
public void testProvideOrder1() {
|
||
|
additionalEndCode = "";
|
||
|
addAdditionalNamespace = false;
|
||
|
// TODO(johnlenz): This test confirms that the constructor (a.b) isn't
|
||
|
// improperly removed, but this result isn't really what we want as the
|
||
|
// reassign of a.b removes the definition of "a.b.c".
|
||
|
test("goog.provide('a.b');" +
|
||
|
"goog.provide('a.b.c');" +
|
||
|
"a.b.c;" +
|
||
|
"a.b = function(x,y) {};",
|
||
|
"var a = {};" +
|
||
|
"a.b = {};" +
|
||
|
"a.b.c = {};" +
|
||
|
"a.b.c;" +
|
||
|
"a.b = function(x,y) {};");
|
||
|
}
|
||
|
|
||
|
// Provide a name after the definition of the class providing the
|
||
|
// parent namespace.
|
||
|
public void testProvideOrder2() {
|
||
|
additionalEndCode = "";
|
||
|
addAdditionalNamespace = false;
|
||
|
// TODO(johnlenz): This test confirms that the constructor (a.b) isn't
|
||
|
// improperly removed, but this result isn't really what we want as
|
||
|
// namespace placeholders for a.b and a.b.c remain.
|
||
|
test("goog.provide('a.b');" +
|
||
|
"goog.provide('a.b.c');" +
|
||
|
"a.b = function(x,y) {};" +
|
||
|
"a.b.c;",
|
||
|
"var a = {};" +
|
||
|
"a.b = {};" +
|
||
|
"a.b.c = {};" +
|
||
|
"a.b = function(x,y) {};" +
|
||
|
"a.b.c;");
|
||
|
}
|
||
|
|
||
|
// Provide a name after the definition of the class providing the
|
||
|
// parent namespace.
|
||
|
public void testProvideOrder3a() {
|
||
|
test("goog.provide('a.b');" +
|
||
|
"a.b = function(x,y) {};" +
|
||
|
"goog.provide('a.b.c');" +
|
||
|
"a.b.c;",
|
||
|
"var a = {};" +
|
||
|
"a.b = function(x,y) {};" +
|
||
|
"a.b.c = {};" +
|
||
|
"a.b.c;");
|
||
|
}
|
||
|
|
||
|
public void testProvideOrder3b() {
|
||
|
additionalEndCode = "";
|
||
|
addAdditionalNamespace = false;
|
||
|
// This tests a cleanly provided name, below a function namespace.
|
||
|
test("goog.provide('a.b');" +
|
||
|
"a.b = function(x,y) {};" +
|
||
|
"goog.provide('a.b.c');" +
|
||
|
"a.b.c;",
|
||
|
"var a = {};" +
|
||
|
"a.b = function(x,y) {};" +
|
||
|
"a.b.c = {};" +
|
||
|
"a.b.c;");
|
||
|
}
|
||
|
|
||
|
public void testProvideOrder4a() {
|
||
|
test("goog.provide('goog.a');" +
|
||
|
"goog.provide('goog.a.b');" +
|
||
|
"if (x) {" +
|
||
|
" goog.a.b = 1;" +
|
||
|
"} else {" +
|
||
|
" goog.a.b = 2;" +
|
||
|
"}",
|
||
|
|
||
|
"goog.a={};" +
|
||
|
"if(x)" +
|
||
|
" goog.a.b=1;" +
|
||
|
"else" +
|
||
|
" goog.a.b=2;");
|
||
|
}
|
||
|
|
||
|
public void testProvideOrder4b() {
|
||
|
additionalEndCode = "";
|
||
|
addAdditionalNamespace = false;
|
||
|
// This tests a cleanly provided name, below a namespace.
|
||
|
test("goog.provide('goog.a');" +
|
||
|
"goog.provide('goog.a.b');" +
|
||
|
"if (x) {" +
|
||
|
" goog.a.b = 1;" +
|
||
|
"} else {" +
|
||
|
" goog.a.b = 2;" +
|
||
|
"}",
|
||
|
|
||
|
"goog.a={};" +
|
||
|
"if(x)" +
|
||
|
" goog.a.b=1;" +
|
||
|
"else" +
|
||
|
" goog.a.b=2;");
|
||
|
}
|
||
|
|
||
|
public void testInvalidProvide() {
|
||
|
test("goog.provide('a.class');", null, INVALID_PROVIDE_ERROR);
|
||
|
}
|
||
|
|
||
|
private static final String METHOD_FORMAT =
|
||
|
"function Foo() {} Foo.prototype.method = function() { %s };";
|
||
|
|
||
|
private static final String FOO_INHERITS =
|
||
|
"goog.inherits(Foo, BaseFoo);";
|
||
|
|
||
|
public void testInvalidBase1() {
|
||
|
test("goog.base(this, 'method');", null, BASE_CLASS_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testInvalidBase2() {
|
||
|
test("function Foo() {}" +
|
||
|
"Foo.method = function() {" +
|
||
|
" goog.base(this, 'method');" +
|
||
|
"};", null, BASE_CLASS_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testInvalidBase3() {
|
||
|
test(String.format(METHOD_FORMAT, "goog.base();"),
|
||
|
null, BASE_CLASS_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testInvalidBase4() {
|
||
|
test(String.format(METHOD_FORMAT, "goog.base(this, 'bar');"),
|
||
|
null, BASE_CLASS_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testInvalidBase5() {
|
||
|
test(String.format(METHOD_FORMAT, "goog.base('foo', 'method');"),
|
||
|
null, BASE_CLASS_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testInvalidBase6() {
|
||
|
test(String.format(METHOD_FORMAT, "goog.base.call(null, this, 'method');"),
|
||
|
null, BASE_CLASS_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testInvalidBase7() {
|
||
|
test("function Foo() { goog.base(this); }",
|
||
|
null, BASE_CLASS_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testInvalidBase8() {
|
||
|
test("var Foo = function() { goog.base(this); }",
|
||
|
null, BASE_CLASS_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testInvalidBase9() {
|
||
|
test("var goog = {}; goog.Foo = function() { goog.base(this); }",
|
||
|
null, BASE_CLASS_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testValidBase1() {
|
||
|
test(String.format(METHOD_FORMAT, "goog.base(this, 'method');"),
|
||
|
String.format(METHOD_FORMAT, "Foo.superClass_.method.call(this)"));
|
||
|
}
|
||
|
|
||
|
public void testValidBase2() {
|
||
|
test(String.format(METHOD_FORMAT, "goog.base(this, 'method', 1, 2);"),
|
||
|
String.format(METHOD_FORMAT,
|
||
|
"Foo.superClass_.method.call(this, 1, 2)"));
|
||
|
}
|
||
|
|
||
|
public void testValidBase3() {
|
||
|
test(String.format(METHOD_FORMAT, "return goog.base(this, 'method');"),
|
||
|
String.format(METHOD_FORMAT,
|
||
|
"return Foo.superClass_.method.call(this)"));
|
||
|
}
|
||
|
|
||
|
public void testValidBase4() {
|
||
|
test("function Foo() { goog.base(this, 1, 2); }" + FOO_INHERITS,
|
||
|
"function Foo() { BaseFoo.call(this, 1, 2); } " + FOO_INHERITS);
|
||
|
}
|
||
|
|
||
|
public void testValidBase5() {
|
||
|
test("var Foo = function() { goog.base(this, 1); };" + FOO_INHERITS,
|
||
|
"var Foo = function() { BaseFoo.call(this, 1); }; " + FOO_INHERITS);
|
||
|
}
|
||
|
|
||
|
public void testValidBase6() {
|
||
|
test("var goog = {}; goog.Foo = function() { goog.base(this); }; " +
|
||
|
"goog.inherits(goog.Foo, goog.BaseFoo);",
|
||
|
"var goog = {}; goog.Foo = function() { goog.BaseFoo.call(this); }; " +
|
||
|
"goog.inherits(goog.Foo, goog.BaseFoo);");
|
||
|
}
|
||
|
|
||
|
public void testImplicitAndExplicitProvide() {
|
||
|
test("var goog = {}; " +
|
||
|
"goog.provide('goog.foo.bar'); goog.provide('goog.foo');",
|
||
|
"var goog = {}; goog.foo = {}; goog.foo.bar = {};");
|
||
|
}
|
||
|
|
||
|
public void testImplicitProvideInIndependentModules() {
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"",
|
||
|
"goog.provide('apps.A');",
|
||
|
"goog.provide('apps.B');"),
|
||
|
new String[] {
|
||
|
"var apps = {};",
|
||
|
"apps.A = {};",
|
||
|
"apps.B = {};",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void testImplicitProvideInIndependentModules2() {
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"goog.provide('apps');",
|
||
|
"goog.provide('apps.foo.A');",
|
||
|
"goog.provide('apps.foo.B');"),
|
||
|
new String[] {
|
||
|
"var apps = {}; apps.foo = {};",
|
||
|
"apps.foo.A = {};",
|
||
|
"apps.foo.B = {};",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void testImplicitProvideInIndependentModules3() {
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"var goog = {};",
|
||
|
"goog.provide('goog.foo.A');",
|
||
|
"goog.provide('goog.foo.B');"),
|
||
|
new String[] {
|
||
|
"var goog = {}; goog.foo = {};",
|
||
|
"goog.foo.A = {};",
|
||
|
"goog.foo.B = {};",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void testProvideInIndependentModules1() {
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"goog.provide('apps');",
|
||
|
"goog.provide('apps.foo');",
|
||
|
"goog.provide('apps.foo.B');"),
|
||
|
new String[] {
|
||
|
"var apps = {}; apps.foo = {};",
|
||
|
"",
|
||
|
"apps.foo.B = {};",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void testProvideInIndependentModules2() {
|
||
|
// TODO(nicksantos): Make this an error.
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"goog.provide('apps');",
|
||
|
"goog.provide('apps.foo'); apps.foo = {};",
|
||
|
"goog.provide('apps.foo.B');"),
|
||
|
new String[] {
|
||
|
"var apps = {};",
|
||
|
"apps.foo = {};",
|
||
|
"apps.foo.B = {};",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void testProvideInIndependentModules2b() {
|
||
|
// TODO(nicksantos): Make this an error.
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"goog.provide('apps');",
|
||
|
"goog.provide('apps.foo'); apps.foo = function() {};",
|
||
|
"goog.provide('apps.foo.B');"),
|
||
|
new String[] {
|
||
|
"var apps = {};",
|
||
|
"apps.foo = function() {};",
|
||
|
"apps.foo.B = {};",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void testProvideInIndependentModules3() {
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"goog.provide('apps');",
|
||
|
"goog.provide('apps.foo.B');",
|
||
|
"goog.provide('apps.foo'); goog.require('apps.foo');"),
|
||
|
new String[] {
|
||
|
"var apps = {}; apps.foo = {};",
|
||
|
"apps.foo.B = {};",
|
||
|
"",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void testProvideInIndependentModules3b() {
|
||
|
// TODO(nicksantos): Make this an error.
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"goog.provide('apps');",
|
||
|
"goog.provide('apps.foo.B');",
|
||
|
"goog.provide('apps.foo'); apps.foo = function() {}; " +
|
||
|
"goog.require('apps.foo');"),
|
||
|
new String[] {
|
||
|
"var apps = {};",
|
||
|
"apps.foo.B = {};",
|
||
|
"apps.foo = function() {};",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void testProvideInIndependentModules4() {
|
||
|
// Regression test for bug 261:
|
||
|
// http://code.google.com/p/closure-compiler/issues/detail?id=261
|
||
|
test(
|
||
|
createModuleStar(
|
||
|
"goog.provide('apps');",
|
||
|
"goog.provide('apps.foo.bar.B');",
|
||
|
"goog.provide('apps.foo.bar.C');"),
|
||
|
new String[] {
|
||
|
"var apps = {};apps.foo = {};apps.foo.bar = {}",
|
||
|
"apps.foo.bar.B = {};",
|
||
|
"apps.foo.bar.C = {};",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void testRequireOfBaseGoog() {
|
||
|
test("goog.require('goog');",
|
||
|
"", MISSING_PROVIDE_ERROR);
|
||
|
}
|
||
|
|
||
|
public void testSourcePositionPreservation() {
|
||
|
test("goog.provide('foo.bar.baz');",
|
||
|
"var foo = {};" +
|
||
|
"foo.bar = {};" +
|
||
|
"foo.bar.baz = {};");
|
||
|
|
||
|
Node root = getLastCompiler().getRoot();
|
||
|
|
||
|
Node fooDecl = findQualifiedNameNode("foo", root);
|
||
|
Node fooBarDecl = findQualifiedNameNode("foo.bar", root);
|
||
|
Node fooBarBazDecl = findQualifiedNameNode("foo.bar.baz", root);
|
||
|
|
||
|
assertEquals(1, fooDecl.getLineno());
|
||
|
assertEquals(14, fooDecl.getCharno());
|
||
|
|
||
|
assertEquals(1, fooBarDecl.getLineno());
|
||
|
assertEquals(18, fooBarDecl.getCharno());
|
||
|
|
||
|
assertEquals(1, fooBarBazDecl.getLineno());
|
||
|
assertEquals(22, fooBarBazDecl.getCharno());
|
||
|
}
|
||
|
|
||
|
public void testNoStubForProvidedTypedef() {
|
||
|
test("goog.provide('x'); /** @typedef {number} */ var x;", "var x;");
|
||
|
}
|
||
|
|
||
|
public void testNoStubForProvidedTypedef2() {
|
||
|
test("goog.provide('x.y'); /** @typedef {number} */ x.y;",
|
||
|
"var x = {}; x.y;");
|
||
|
}
|
||
|
|
||
|
public void testNoStubForProvidedTypedef4() {
|
||
|
test("goog.provide('x.y.z'); /** @typedef {number} */ x.y.z;",
|
||
|
"var x = {}; x.y = {}; x.y.z;");
|
||
|
}
|
||
|
|
||
|
public void testProvideRequireSameFile() {
|
||
|
test("goog.provide('x');\ngoog.require('x');", "var x = {};");
|
||
|
}
|
||
|
}
|