This repository has been archived on 2023-06-18. You can view files and clone it, but cannot push or open issues or pull requests.
ima02/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/ReplaceStringsTest.java

423 lines
13 KiB
Java
Raw Permalink Normal View History

2023-04-25 11:33:41 +00:00
/*
* Copyright 2010 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.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.javascript.jscomp.ReplaceStrings.Result;
import com.google.javascript.rhino.Node;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* Tests for {@link ReplaceStrings}.
*
*/
public class ReplaceStringsTest extends CompilerTestCase {
private ReplaceStrings pass;
private Set<String> reserved;
private VariableMap previous;
private final static String EXTERNS =
"var goog = {};\n" +
"goog.debug = {};\n" +
"/** @constructor */\n" +
"goog.debug.Trace = function() {};\n" +
"goog.debug.Trace.startTracer = function (var_args) {};\n" +
"/** @constructor */\n" +
"goog.debug.Logger = function() {};\n" +
"goog.debug.Logger.prototype.info = function(msg, opt_ex) {};\n" +
"/**\n" +
" * @param {string} name\n" +
" * @return {!goog.debug.Logger}\n" +
" */\n" +
"goog.debug.Logger.getLogger = function(name){};\n";
public ReplaceStringsTest() {
super(EXTERNS, true);
enableNormalize();
}
@Override
protected CompilerOptions getOptions() {
CompilerOptions options = super.getOptions();
options.setWarningLevel(
DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.OFF);
return options;
}
@Override
protected void setUp() throws Exception {
super.setUp();
super.enableLineNumberCheck(false);
super.enableTypeCheck(CheckLevel.OFF);
reserved = Collections.emptySet();
previous = null;
}
@Override
public CompilerPass getProcessor(final Compiler compiler) {
List<String> names = Lists.newArrayList(
"Error(?)",
"goog.debug.Trace.startTracer(*)",
"goog.debug.Logger.getLogger(?)",
"goog.debug.Logger.prototype.info(?)"
);
pass = new ReplaceStrings(compiler, "`", names, reserved, previous);
return new CompilerPass() {
@Override
public void process(Node externs, Node js) {
new CollapseProperties(compiler, true, true).process(externs, js);
pass.process(externs, js);
}
};
}
@Override
public int getNumRepetitions() {
// This compiler pass is not idempotent and should only be run over a
// parse tree once.
return 1;
}
public void testStable1() {
previous = VariableMap.fromMap(ImmutableMap.of("previous","xyz"));
testDebugStrings(
"Error('xyz');",
"Error('previous');",
(new String[] { "previous", "xyz" }));
reserved = ImmutableSet.of("a", "b", "previous");
testDebugStrings(
"Error('xyz');",
"Error('c');",
(new String[] { "c", "xyz" }));
}
public void testStable2() {
// Two things happen here:
// 1) a previously used name "a" is not used for another string, "b" is
// chosen instead.
// 2) a previously used name "a" is dropped from the output map if
// it isn't used.
previous = VariableMap.fromMap(ImmutableMap.of("a","unused"));
testDebugStrings(
"Error('xyz');",
"Error('b');",
(new String[] { "b", "xyz" }));
}
public void testThrowError1() {
testDebugStrings(
"throw Error('xyz');",
"throw Error('a');",
(new String[] { "a", "xyz" }));
previous = VariableMap.fromMap(ImmutableMap.of("previous","xyz"));
testDebugStrings(
"throw Error('xyz');",
"throw Error('previous');",
(new String[] { "previous", "xyz" }));
}
public void testThrowError2() {
testDebugStrings(
"throw Error('x' +\n 'yz');",
"throw Error('a');",
(new String[] { "a", "xyz" }));
}
public void testThrowError3() {
testDebugStrings(
"throw Error('Unhandled mail' + ' search type ' + type);",
"throw Error('a' + '`' + type);",
(new String[] { "a", "Unhandled mail search type `" }));
}
public void testThrowError4() {
testDebugStrings(
"/** @constructor */\n" +
"var A = function() {};\n" +
"A.prototype.m = function(child) {\n" +
" if (this.haveChild(child)) {\n" +
" throw Error('Node: ' + this.getDataPath() +\n" +
" ' already has a child named ' + child);\n" +
" } else if (child.parentNode) {\n" +
" throw Error('Node: ' + child.getDataPath() +\n" +
" ' already has a parent');\n" +
" }\n" +
" child.parentNode = this;\n" +
"};",
"var A = function(){};\n" +
"A.prototype.m = function(child) {\n" +
" if (this.haveChild(child)) {\n" +
" throw Error('a' + '`' + this.getDataPath() + '`' + child);\n" +
" } else if (child.parentNode) {\n" +
" throw Error('b' + '`' + child.getDataPath());\n" +
" }\n" +
" child.parentNode = this;\n" +
"};",
(new String[] {
"a",
"Node: ` already has a child named `",
"b",
"Node: ` already has a parent",
}));
}
public void testThrowNonStringError() {
// No replacement is done when an error is neither a string literal nor
// a string concatenation expression.
testDebugStrings(
"throw Error(x('abc'));",
"throw Error(x('abc'));",
(new String[] { }));
}
public void testThrowConstStringError() {
testDebugStrings(
"var AA = 'uvw', AB = 'xyz'; throw Error(AB);",
"var AA = 'uvw', AB = 'xyz'; throw Error('a');",
(new String [] { "a", "xyz" }));
}
public void testThrowNewError1() {
testDebugStrings(
"throw new Error('abc');",
"throw new Error('a');",
(new String[] { "a", "abc" }));
}
public void testThrowNewError2() {
testDebugStrings(
"throw new Error();",
"throw new Error();",
new String[] {});
}
public void testStartTracer1() {
testDebugStrings(
"goog.debug.Trace.startTracer('HistoryManager.updateHistory');",
"goog.debug.Trace.startTracer('a');",
(new String[] { "a", "HistoryManager.updateHistory" }));
}
public void testStartTracer2() {
testDebugStrings(
"goog$debug$Trace.startTracer('HistoryManager', 'updateHistory');",
"goog$debug$Trace.startTracer('a', 'b');",
(new String[] {
"a", "HistoryManager",
"b", "updateHistory" }));
}
public void testStartTracer3() {
testDebugStrings(
"goog$debug$Trace.startTracer('ThreadlistView',\n" +
" 'Updating ' + array.length + ' rows');",
"goog$debug$Trace.startTracer('a', 'b' + '`' + array.length);",
new String[] { "a", "ThreadlistView", "b", "Updating ` rows" });
}
public void testStartTracer4() {
testDebugStrings(
"goog.debug.Trace.startTracer(s, 'HistoryManager.updateHistory');",
"goog.debug.Trace.startTracer(s, 'a');",
(new String[] { "a", "HistoryManager.updateHistory" }));
}
public void testLoggerInitialization() {
testDebugStrings(
"goog$debug$Logger$getLogger('my.app.Application');",
"goog$debug$Logger$getLogger('a');",
(new String[] { "a", "my.app.Application" }));
}
public void testLoggerOnObject1() {
testDebugStrings(
"var x = {};" +
"x.logger_ = goog.debug.Logger.getLogger('foo');" +
"x.logger_.info('Some message');",
"var x$logger_ = goog.debug.Logger.getLogger('a');" +
"x$logger_.info('b');",
new String[] {
"a", "foo",
"b", "Some message"});
}
// Non-matching "info" property.
public void testLoggerOnObject2() {
test(
"var x = {};" +
"x.info = function(a) {};" +
"x.info('Some message');",
"var x$info = function(a) {};" +
"x$info('Some message');");
}
// Non-matching "info" prototype property.
public void testLoggerOnObject3a() {
testSame(
"/** @constructor */\n" +
"var x = function() {};\n" +
"x.prototype.info = function(a) {};" +
"(new x).info('Some message');");
}
// Non-matching "info" prototype property.
public void testLoggerOnObject3b() {
testSame(
"/** @constructor */\n" +
"var x = function() {};\n" +
"x.prototype.info = function(a) {};" +
"var y = (new x); this.info('Some message');");
}
// Non-matching "info" property on "NoObject" type.
public void testLoggerOnObject4() {
testSame("(new x).info('Some message');");
}
// Non-matching "info" property on "UnknownObject" type.
public void testLoggerOnObject5() {
testSame("my$Thing.logger_.info('Some message');");
}
public void testLoggerOnVar() {
testDebugStrings(
"var logger = goog.debug.Logger.getLogger('foo');" +
"logger.info('Some message');",
"var logger = goog.debug.Logger.getLogger('a');" +
"logger.info('b');",
new String[] {
"a", "foo",
"b", "Some message"});
}
public void testLoggerOnThis() {
testDebugStrings(
"function f() {" +
" this.logger_ = goog.debug.Logger.getLogger('foo');" +
" this.logger_.info('Some message');" +
"}",
"function f() {" +
" this.logger_ = goog.debug.Logger.getLogger('a');" +
" this.logger_.info('b');" +
"}",
new String[] {
"a", "foo",
"b", "Some message"});
}
public void testRepeatedErrorString1() {
testDebugStrings(
"Error('abc');Error('def');Error('abc');",
"Error('a');Error('b');Error('a');",
(new String[] { "a", "abc", "b", "def" }));
}
public void testRepeatedErrorString2() {
testDebugStrings(
"Error('a:' + u + ', b:' + v); Error('a:' + x + ', b:' + y);",
"Error('a' + '`' + u + '`' + v); Error('a' + '`' + x + '`' + y);",
(new String[] { "a", "a:`, b:`" }));
}
public void testRepeatedErrorString3() {
testDebugStrings(
"var AB = 'b'; throw Error(AB); throw Error(AB);",
"var AB = 'b'; throw Error('a'); throw Error('a');",
(new String[] { "a", "b" }));
}
public void testRepeatedTracerString() {
testDebugStrings(
"goog$debug$Trace.startTracer('A', 'B', 'A');",
"goog$debug$Trace.startTracer('a', 'b', 'a');",
(new String[] { "a", "A", "b", "B" }));
}
public void testRepeatedLoggerString() {
testDebugStrings(
"goog$debug$Logger$getLogger('goog.net.XhrTransport');" +
"goog$debug$Logger$getLogger('my.app.Application');" +
"goog$debug$Logger$getLogger('my.app.Application');",
"goog$debug$Logger$getLogger('a');" +
"goog$debug$Logger$getLogger('b');" +
"goog$debug$Logger$getLogger('b');",
new String[] {
"a", "goog.net.XhrTransport","b", "my.app.Application" });
}
public void testRepeatedStringsWithDifferentMethods() {
test(
"throw Error('A');"
+ "goog$debug$Trace.startTracer('B', 'A');"
+ "goog$debug$Logger$getLogger('C');"
+ "goog$debug$Logger$getLogger('B');"
+ "goog$debug$Logger$getLogger('A');"
+ "throw Error('D');"
+ "throw Error('C');"
+ "throw Error('B');"
+ "throw Error('A');",
"throw Error('a');"
+ "goog$debug$Trace.startTracer('b', 'a');"
+ "goog$debug$Logger$getLogger('c');"
+ "goog$debug$Logger$getLogger('b');"
+ "goog$debug$Logger$getLogger('a');"
+ "throw Error('d');"
+ "throw Error('c');"
+ "throw Error('b');"
+ "throw Error('a');");
}
public void testReserved() {
testDebugStrings(
"throw Error('xyz');",
"throw Error('a');",
(new String[] { "a", "xyz" }));
reserved = ImmutableSet.of("a", "b", "c");
testDebugStrings(
"throw Error('xyz');",
"throw Error('d');",
(new String[] { "d", "xyz" }));
}
private void testDebugStrings(String js, String expected,
String[] substitutedStrings) {
// Verify that the strings are substituted correctly in the JS code.
test(js, expected);
List<Result> results = pass.getResult();
assertTrue(substitutedStrings.length % 2 == 0);
assertEquals(substitutedStrings.length/2, results.size());
// Verify that substituted strings are decoded correctly.
for (int i = 0; i < substitutedStrings.length; i += 2) {
Result result = results.get(i/2);
String original = substitutedStrings[i + 1];
assertEquals(original, result.original);
String replacement = substitutedStrings[i];
assertEquals(replacement, result.replacement);
}
}
}