/*
* Copyright 2009 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 static com.google.javascript.jscomp.ReplaceIdGenerators.INVALID_GENERATOR_PARAMETER;
import com.google.common.collect.ImmutableMap;
/**
* Tests for {@link ReplaceIdGenerators}.
*
*/
public final class ReplaceIdGeneratorsTest extends Es6CompilerTestCase {
private boolean generatePseudoNames = false;
private ReplaceIdGenerators lastPass = null;
private String previousMappings = null;
@Override
protected CompilerPass getProcessor(final Compiler compiler) {
RenamingMap idTestMap = new RenamingMap() {
private final ImmutableMap<String, String> map = ImmutableMap.of(
"foo", ":foo:",
"bar", ":bar:");
@Override
public String get(String value) {
String replacement = map.get(value);
return replacement != null ? replacement : "unknown:" + value;
}
};
RenamingMap gen = new UniqueRenamingToken();
lastPass = new ReplaceIdGenerators(
compiler,
new ImmutableMap.Builder<String, RenamingMap>()
.put("goog.events.getUniqueId", gen)
.put("goog.place.getUniqueId", gen)
.put("id", idTestMap)
.put("get.id", idTestMap)
.build(),
generatePseudoNames,
previousMappings,
null /* xidHashFunction */);
return lastPass;
}
@Override
protected void setUp() throws Exception {
super.setUp();
generatePseudoNames = false;
previousMappings = null;
}
@Override
protected int getNumRepetitions() {
return 1;
}
public void testBackwardCompat() {
test("foo.bar = goog.events.getUniqueId('foo_bar')",
"foo.bar = 'a'",
"foo.bar = 'foo_bar$0'");
}
public void testSerialization1() {
testMap(
LINE_JOINER.join(
"var x = goog.events.getUniqueId('xxx');",
"var y = goog.events.getUniqueId('yyy');"),
LINE_JOINER.join(
"var x = 'a';",
"var y = 'b';"),
LINE_JOINER.join(
"[goog.events.getUniqueId]",
"",
"a:testcode:1:32",
"b:testcode:2:32",
"",
""));
}
public void testSerialization2() {
testMap(
LINE_JOINER.join(
"/** @consistentIdGenerator */",
"id = function() {};",
"f1 = id('f1');",
"f1 = id('f1')"),
LINE_JOINER.join(
"/** @consistentIdGenerator */",
"id = function() {};",
"f1 = 'a';",
"f1 = 'a'"),
LINE_JOINER.join(
"[id]",
"",
"a:f1",
"",
""));
}
public void testReusePreviousSerialization1() {
previousMappings = LINE_JOINER.join(
"[goog.events.getUniqueId]",
"",
"previous1:testcode:1:32",
"previous2:testcode:2:32",
"",
"[goog.place.getUniqueId]",
"",
"");
testMap("var x = goog.events.getUniqueId('xxx');\n" +
"var y = goog.events.getUniqueId('yyy');\n",
"var x = 'previous1';\n" +
"var y = 'previous2';\n",
"[goog.events.getUniqueId]\n" +
"\n" +
"previous1:testcode:1:32\n" +
"previous2:testcode:2:32\n" +
"\n");
}
public void testReusePreviousSerialization2() {
previousMappings =
"[goog.events.getUniqueId]\n" +
"\n" +
"a:testcode:1:32\n" +
"b:testcode:2:32\n" +
"\n" +
"[goog.place.getUniqueId]\n" +
"\n" +
"\n";
testMap(
"var x = goog.events.getUniqueId('xxx');\n" +
"\n" + // new line to change location
"var y = goog.events.getUniqueId('yyy');\n",
"var x = 'a';\n" +
"var y = 'c';\n",
"[goog.events.getUniqueId]\n" +
"\n" +
"a:testcode:1:32\n" +
"c:testcode:3:32\n" +
"\n");
}
public void testReusePreviousSerializationConsistent1() {
previousMappings =
"[id]\n" +
"\n" +
"a:f1\n" +
"\n";
testMap(
LINE_JOINER.join(
"/** @consistentIdGenerator */ id = function() {};",
"f1 = id('f1');",
"f1 = id('f1')"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ id = function() {};",
"f1 = 'a';",
"f1 = 'a'"),
"[id]\n" + "\n" + "a:f1\n" + "\n");
}
public void testSimple() {
test(
LINE_JOINER.join(
"/** @idGenerator */ foo.getUniqueId = function() {};",
"foo.bar = foo.getUniqueId('foo_bar')"),
LINE_JOINER.join(
"/** @idGenerator */ foo.getUniqueId = function() {};",
"foo.bar = 'a'"),
LINE_JOINER.join(
"/** @idGenerator */ foo.getUniqueId = function() {};",
"foo.bar = 'foo_bar$0'"));
test(
LINE_JOINER.join(
"/** @idGenerator */ goog.events.getUniqueId = function() {};",
"foo1 = goog.events.getUniqueId('foo1');",
"foo1 = goog.events.getUniqueId('foo1');"),
LINE_JOINER.join(
"/** @idGenerator */ goog.events.getUniqueId = function() {};",
"foo1 = 'a';",
"foo1 = 'b';"),
LINE_JOINER.join(
"/** @idGenerator */ goog.events.getUniqueId = function() {};",
"foo1 = 'foo1$0';",
"foo1 = 'foo1$1';"));
}
public void testObjectLit() {
test(
LINE_JOINER.join(
"/** @idGenerator */ goog.id = function() {};",
"things = goog.id({foo1: 'test', 'foo bar': 'test'})"),
LINE_JOINER.join(
"/** @idGenerator */ goog.id = function() {};",
"things = {'a': 'test', 'b': 'test'}"),
LINE_JOINER.join(
"/** @idGenerator */ goog.id = function() {};",
"things = {'foo1$0': 'test', 'foo bar$1': 'test'}"));
}
public void testObjectLit_mapped() {
testNonPseudoSupportingGenerator(
LINE_JOINER.join(
"/** @idGenerator {mapped} */ id = function() {};",
"things = id({foo: 'test', 'bar': 'test'})"),
LINE_JOINER.join(
"/** @idGenerator {mapped} */ id = function() {};",
"things = {':foo:': 'test', ':bar:': 'test'}"));
}
public void testObjectLit_xid() {
testNonPseudoSupportingGenerator(
LINE_JOINER.join(
"/** @idGenerator {xid} */ xid.object = function() {};",
"things = xid.object({foo: 'test', 'value': 'test'})"),
LINE_JOINER.join(
"/** @idGenerator {xid} */ xid.object = function() {};",
"things = {'QB6rXc': 'test', 'b6Lt6c': 'test'}"));
}
public void testObjectLit_empty() {
test(
"/** @idGenerator */ goog.id = function() {}; things = goog.id({})",
"/** @idGenerator */ goog.id = function() {}; things = {}",
"/** @idGenerator */ goog.id = function() {}; things = {}");
}
public void testObjectLit_function() {
test(
LINE_JOINER.join(
"/** @idGenerator */ goog.id = function() {};",
"things = goog.id({foo: function() {}})"),
LINE_JOINER.join(
"/** @idGenerator */ goog.id = function() {};",
"things = {'a': function() {}}"),
LINE_JOINER.join(
"/** @idGenerator */ goog.id = function() {};",
"things = {'foo$0': function() {}}"));
testEs6(
LINE_JOINER.join(
"/** @idGenerator */ goog.id = function() {};",
"things = goog.id({foo: function*() {}})"),
LINE_JOINER.join(
"/** @idGenerator */ goog.id = function() {};",
"things = {'a': function*() {}}"),
LINE_JOINER.join(
"/** @idGenerator */ goog.id = function() {};",
"things = {'foo$0': function*() {}}"));
}
public void testObjectLit_ES6() {
testErrorEs6(LINE_JOINER.join(
"/** @idGenerator */",
"goog.id = function() {};",
"things = goog.id({fooX() {}})"),
ReplaceIdGenerators.SHORTHAND_FUNCTION_NOT_SUPPORTED_IN_ID_GEN);
testErrorEs6(LINE_JOINER.join(
"/** @idGenerator */ ",
"goog.id = function() {};",
"things = goog.id({shorthand})"),
ReplaceIdGenerators.SHORTHAND_ASSIGNMENT_NOT_SUPPORTED_IN_ID_GEN);
testErrorEs6(LINE_JOINER.join(
"/** @idGenerator */",
"goog.id = function() {};",
"things = goog.id({['fooX']: 'test'})"),
ReplaceIdGenerators.COMPUTED_PROP_NOT_SUPPORTED_IN_ID_GEN);
}
public void testClass() {
testSameEs6("", LINE_JOINER.join(
"/** @idGenerator */",
"goog.id = function() {};",
"things = goog.id(class fooBar{})"),
ReplaceIdGenerators.INVALID_GENERATOR_PARAMETER);
}
public void testSimpleConsistent() {
test(
"/** @consistentIdGenerator */ id = function() {}; foo.bar = id('foo_bar')",
"/** @consistentIdGenerator */ id = function() {}; foo.bar = 'a'",
"/** @consistentIdGenerator */ id = function() {}; foo.bar = 'foo_bar$0'");
test(
"/** @consistentIdGenerator */ id = function() {}; f1 = id('f1'); f1 = id('f1')",
"/** @consistentIdGenerator */ id = function() {}; f1 = 'a'; f1 = 'a'",
"/** @consistentIdGenerator */ id = function() {}; f1 = 'f1$0'; f1 = 'f1$0'");
test(
LINE_JOINER.join(
"/** @consistentIdGenerator */ id = function() {};",
"f1 = id('f1');",
"f1 = id('f1');",
"f1 = id('f1')"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ id = function() {};",
"f1 = 'a';",
"f1 = 'a';",
"f1 = 'a'"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ id = function() {};",
"f1 = 'f1$0';",
"f1 = 'f1$0';",
"f1 = 'f1$0'"));
}
public void testSimpleStable() {
testNonPseudoSupportingGenerator(
"/** @stableIdGenerator */ id = function() {};" + "foo.bar = id('foo_bar')",
"/** @stableIdGenerator */ id = function() {};" + "foo.bar = '125lGg'");
testNonPseudoSupportingGenerator(
"/** @stableIdGenerator */ id = function() {};" + "f1 = id('f1');" + "f1 = id('f1')",
"/** @stableIdGenerator */ id = function() {};" + "f1 = 'AAAMiw';" + "f1 = 'AAAMiw'");
}
public void testSimpleXid() {
testNonPseudoSupportingGenerator(
LINE_JOINER.join("/** @idGenerator {xid} */ id = function() {};", "foo.bar = id('foo')"),
LINE_JOINER.join("/** @idGenerator {xid} */ id = function() {};", "foo.bar = 'QB6rXc'"));
testNonPseudoSupportingGenerator(
LINE_JOINER.join(
"/** @idGenerator {xid} */ id = function() {};", "f1 = id('foo');", "f1 = id('foo')"),
LINE_JOINER.join(
"/** @idGenerator {xid} */ id = function() {};", "f1 = 'QB6rXc';", "f1 = 'QB6rXc'"));
}
public void testVar() {
test(
LINE_JOINER.join(
"/** @consistentIdGenerator */ var id = function() {};", "foo.bar = id('foo_bar')"),
LINE_JOINER.join("/** @consistentIdGenerator */ var id = function() {};", "foo.bar = 'a'"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ var id = function() {};", "foo.bar = 'foo_bar$0'"));
testNonPseudoSupportingGenerator(
LINE_JOINER.join(
"/** @stableIdGenerator */ var id = function() {};", "foo.bar = id('foo_bar')"),
LINE_JOINER.join(
"/** @stableIdGenerator */ var id = function() {};", "foo.bar = '125lGg'"));
}
public void testLet() {
testEs6(
LINE_JOINER.join(
"/** @consistentIdGenerator */ let id = function() {};", "foo.bar = id('foo_bar')"),
LINE_JOINER.join("/** @consistentIdGenerator */ let id = function() {};", "foo.bar = 'a'"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ let id = function() {};", "foo.bar = 'foo_bar$0'"));
testNonPseudoSupportingGeneratorEs6(
"/** @stableIdGenerator */ let id = function() {};" + "foo.bar = id('foo_bar')",
"/** @stableIdGenerator */ let id = function() {};" + "foo.bar = '125lGg'");
}
public void testConst() {
testEs6(
LINE_JOINER.join(
"/** @consistentIdGenerator */ const id = function() {};", "foo.bar = id('foo_bar')"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ const id = function() {};", "foo.bar = 'a'"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ const id = function() {};", "foo.bar = 'foo_bar$0'"));
testNonPseudoSupportingGeneratorEs6(
LINE_JOINER.join(
"/** @stableIdGenerator */ const id = function() {};", "foo.bar = id('foo_bar')"),
LINE_JOINER.join(
"/** @stableIdGenerator */ const id = function() {};", "foo.bar = '125lGg'"));
}
public void testInObjLit() {
test(
LINE_JOINER.join(
"/** @consistentIdGenerator */ get.id = function() {};",
"foo.bar = {a: get.id('foo_bar')}"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ get.id = function() {};", "foo.bar = {a: 'a'}"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ get.id = function() {};", "foo.bar = {a: 'foo_bar$0'}"));
testNonPseudoSupportingGenerator(
LINE_JOINER.join(
"/** @stableIdGenerator */ get.id = function() {};",
"foo.bar = {a: get.id('foo_bar')}"),
LINE_JOINER.join(
"/** @stableIdGenerator */ get.id = function() {};", "foo.bar = {a: '125lGg'}"));
testNonPseudoSupportingGenerator(
LINE_JOINER.join(
"/** @idGenerator {xid} */ get.id = function() {};", "foo.bar = {a: get.id('foo')}"),
LINE_JOINER.join(
"/** @idGenerator {xid} */ get.id = function() {};", "foo.bar = {a: 'QB6rXc'}"));
}
public void testInObjLit_mapped() {
test(
LINE_JOINER.join(
"/** @idGenerator {mapped}*/ id = function() {};", "foo.bar = {a: id('foo')}"),
LINE_JOINER.join(
"/** @idGenerator {mapped}*/ id = function() {};", "foo.bar = {a: ':foo:'}"),
LINE_JOINER.join(
"/** @idGenerator {mapped}*/ id = function() {};", "foo.bar = {a: ':foo:'}"));
}
public void testMapped() {
test(
LINE_JOINER.join("/** @idGenerator {mapped}*/ id = function() {};", "foo.bar = id('foo');"),
LINE_JOINER.join("/** @idGenerator {mapped}*/ id = function() {};", "foo.bar = ':foo:';"),
LINE_JOINER.join("/** @idGenerator {mapped}*/ id = function() {};", "foo.bar = ':foo:';"));
}
public void testMappedMap() {
testMap(
LINE_JOINER.join(
"/** @idGenerator {mapped}*/ id = function() {};",
"foo.bar = id('foo');",
"foo.bar = id('foo');"),
LINE_JOINER.join(
"/** @idGenerator {mapped}*/id = function() {};",
"foo.bar = ':foo:';",
"foo.bar = ':foo:';"),
LINE_JOINER.join("[id]", "", ":foo::foo", "", ""));
}
public void testMapped2() {
test(
LINE_JOINER.join(
"/** @idGenerator {mapped}*/ id = function() {};",
"foo.bar = function() { return id('foo'); };"),
LINE_JOINER.join(
"/** @idGenerator {mapped}*/ id = function() {};",
"foo.bar = function() { return ':foo:'; };"),
LINE_JOINER.join(
"/** @idGenerator {mapped}*/ id = function() {};",
"foo.bar = function() { return ':foo:'; };"));
}
public void testTwoGenerators() {
test(
LINE_JOINER.join(
"/** @idGenerator */ var id1 = function() {};",
"/** @idGenerator */ var id2 = function() {};",
"f1 = id1('1');",
"f2 = id1('1');",
"f3 = id2('1');",
"f4 = id2('1');"),
LINE_JOINER.join(
"/** @idGenerator */ var id1 = function() {};",
"/** @idGenerator */ var id2 = function() {};",
"f1 = 'a';",
"f2 = 'b';",
"f3 = 'a';",
"f4 = 'b';"),
LINE_JOINER.join(
"/** @idGenerator */ var id1 = function() {};",
"/** @idGenerator */ var id2 = function() {};",
"f1 = '1$0';",
"f2 = '1$1';",
"f3 = '1$0';",
"f4 = '1$1';"));
}
public void testMixedGenerators() {
test(
LINE_JOINER.join(
"/** @idGenerator */ var id1 = function() {};",
"/** @consistentIdGenerator */ var id2 = function() {};",
"/** @stableIdGenerator */ var id3 = function() {};",
"f1 = id1('1');",
"f2 = id1('1');",
"f3 = id2('1');",
"f4 = id2('1');",
"f5 = id3('1');",
"f6 = id3('1');"),
LINE_JOINER.join(
"/** @idGenerator */ var id1 = function() {};",
"/** @consistentIdGenerator */ var id2 = function() {};",
"/** @stableIdGenerator */ var id3 = function() {};",
"f1 = 'a';",
"f2 = 'b';",
"f3 = 'a';",
"f4 = 'a';",
"f5 = 'AAAAMQ';",
"f6 = 'AAAAMQ';"),
LINE_JOINER.join(
"/** @idGenerator */ var id1 = function() {};",
"/** @consistentIdGenerator */ var id2 = function() {};",
"/** @stableIdGenerator */ var id3 = function() {};",
"f1 = '1$0';",
"f2 = '1$1';",
"f3 = '1$0';",
"f4 = '1$0';",
"f5 = 'AAAAMQ';",
"f6 = 'AAAAMQ';"));
}
public void testNonLiteralParam1() {
testSame("/** @idGenerator */ var id = function() {}; "
+ "var x = 'foo';"
+ "id(x);",
ReplaceIdGenerators.INVALID_GENERATOR_PARAMETER);
}
public void testNonLiteralParam2() {
testSame("/** @idGenerator */ var id = function() {}; "
+ "id('foo' + 'bar');",
ReplaceIdGenerators.INVALID_GENERATOR_PARAMETER);
}
public void testLocalCall() {
testError("/** @idGenerator */ var id = function() {}; "
+ "function Foo() { id('foo'); }",
ReplaceIdGenerators.NON_GLOBAL_ID_GENERATOR_CALL);
}
public void testConditionalCall() {
testError(
LINE_JOINER.join(
"/** @idGenerator */", "var id = function() {}; ", "while(0){ id('foo');}"),
ReplaceIdGenerators.CONDITIONAL_ID_GENERATOR_CALL);
testError(
LINE_JOINER.join("/** @idGenerator */", "var id = function() {}; ", "for(;;){ id('foo');}"),
ReplaceIdGenerators.CONDITIONAL_ID_GENERATOR_CALL);
testError("/** @idGenerator */ var id = function() {}; "
+ "if(x) id('foo');",
ReplaceIdGenerators.CONDITIONAL_ID_GENERATOR_CALL);
test(
LINE_JOINER.join(
"/** @consistentIdGenerator */ var id = function() {};",
"function fb() {foo.bar = id('foo_bar')}"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ var id = function() {};",
"function fb() {foo.bar = 'a'}"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ var id = function() {};",
"function fb() {foo.bar = 'foo_bar$0'}"));
testNonPseudoSupportingGenerator(
LINE_JOINER.join(
"/** @stableIdGenerator */ var id = function() {};",
"function fb() {foo.bar = id('foo_bar')}"),
LINE_JOINER.join(
"/** @stableIdGenerator */ var id = function() {};",
"function fb() {foo.bar = '125lGg'}"));
testErrorEs6(
LINE_JOINER.join(
"/** @idGenerator */",
"var id = function() {}; ",
"for(x of [1, 2, 3]){ id('foo');}"),
ReplaceIdGenerators.CONDITIONAL_ID_GENERATOR_CALL);
}
public void testConflictingIdGenerator() {
testError("/** @idGenerator \n @consistentIdGenerator \n*/"
+ "var id = function() {}; ",
ReplaceIdGenerators.CONFLICTING_GENERATOR_TYPE);
testError("/** @stableIdGenerator \n @idGenerator \n*/"
+ "var id = function() {}; ",
ReplaceIdGenerators.CONFLICTING_GENERATOR_TYPE);
testError("/** @stableIdGenerator \n "
+ "@consistentIdGenerator \n*/"
+ "var id = function() {}; ",
ReplaceIdGenerators.CONFLICTING_GENERATOR_TYPE);
test(
LINE_JOINER.join(
"/** @consistentIdGenerator */ var id = function() {};",
"if (x) {foo.bar = id('foo_bar')}"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ var id = function() {};", "if (x) {foo.bar = 'a'}"),
LINE_JOINER.join(
"/** @consistentIdGenerator */ var id = function() {};",
"if (x) {foo.bar = 'foo_bar$0'}"));
}
public void testUnknownMapping() {
testSame(LINE_JOINER.join(
"/** @idGenerator {mapped} */",
"var unknownId = function() {};",
"function Foo() { unknownId('foo'); }"),
ReplaceIdGenerators.MISSING_NAME_MAP_FOR_GENERATOR);
}
public void testBadGenerator1() {
testSame("/** @idGenerator */ id = function() {};" +
"foo.bar = id()",
INVALID_GENERATOR_PARAMETER);
}
public void testBadGenerator2() {
testSame("/** @consistentIdGenerator */ id = function() {};" +
"foo.bar = id()",
INVALID_GENERATOR_PARAMETER);
}
private void testMap(String code, String expected, String expectedMap) {
test(code, expected);
assertEquals(expectedMap, lastPass.getSerializedIdMappings());
}
private void test(String code, String expected, String expectedPseudo) {
generatePseudoNames = false;
test(code, expected);
generatePseudoNames = true;
test(code, expectedPseudo);
}
private void testEs6(String code, String expected, String expectedPseudo) {
generatePseudoNames = false;
testEs6(code, expected);
generatePseudoNames = true;
testEs6(code, expectedPseudo);
}
private void testNonPseudoSupportingGenerator(String code, String expected) {
generatePseudoNames = false;
test(code, expected);
generatePseudoNames = true;
test(code, expected);
}
private void testNonPseudoSupportingGeneratorEs6(String code, String expected) {
generatePseudoNames = false;
testEs6(code, expected);
generatePseudoNames = true;
testEs6(code, expected);
}
}