263 lines
8.7 KiB
Java
263 lines
8.7 KiB
Java
/*
|
|
* Copyright 2008 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.ImmutableList;
|
|
import com.google.javascript.rhino.Node;
|
|
|
|
import java.io.StringReader;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Tests for {@link InstrumentFunctions}
|
|
*
|
|
*/
|
|
public class InstrumentFunctionsTest extends CompilerTestCase {
|
|
private String instrumentationPb;
|
|
|
|
public InstrumentFunctionsTest() {
|
|
this.instrumentationPb = null;
|
|
}
|
|
|
|
@Override
|
|
protected void setUp() {
|
|
super.enableLineNumberCheck(false);
|
|
this.instrumentationPb = null;
|
|
}
|
|
|
|
@Override
|
|
protected CompilerPass getProcessor(Compiler compiler) {
|
|
return new NameAndInstrumentFunctions(compiler);
|
|
}
|
|
|
|
@Override
|
|
protected int getNumRepetitions() {
|
|
// This pass is not idempotent.
|
|
return 1;
|
|
}
|
|
|
|
public void testInstrument() {
|
|
final String kPreamble =
|
|
"var $$toRemoveDefinition1, $$notToRemove;\n" +
|
|
"var $$toRemoveDefinition2, $$toRemoveDefinition3;\n";
|
|
|
|
// build instrumentation template and init code strings for use in
|
|
// tests below.
|
|
List<String> initCodeList = ImmutableList.of(
|
|
"var $$Table = [];",
|
|
"function $$TestDefine(id) {",
|
|
" $$Table[id] = 0;",
|
|
"};",
|
|
"function $$TestInstrument(id) {",
|
|
" $$Table[id]++;",
|
|
"};");
|
|
StringBuilder initCodeBuilder = new StringBuilder();
|
|
StringBuilder pbBuilder = new StringBuilder();
|
|
for (String line : initCodeList) {
|
|
initCodeBuilder.append(line).append("\n");
|
|
pbBuilder.append("init: \"").append(line).append("\"\n");
|
|
}
|
|
|
|
pbBuilder.append("report_call: \"$$testInstrument\"")
|
|
.append("report_defined: \"$$testDefine\"")
|
|
.append("declaration_to_remove: \"$$toRemoveDefinition1\"")
|
|
.append("declaration_to_remove: \"$$toRemoveDefinition2\"")
|
|
.append("declaration_to_remove: \"$$toRemoveDefinition3\"");
|
|
|
|
final String initCode = initCodeBuilder.toString();
|
|
this.instrumentationPb = pbBuilder.toString();
|
|
|
|
// Test basic instrumentation
|
|
test("function a(){b}",
|
|
initCode + "$$testDefine(0);" +
|
|
"function a(){$$testInstrument(0);b}");
|
|
|
|
// Test declaration_to_remove
|
|
test(kPreamble + "function a(){b}",
|
|
initCode +
|
|
"$$testDefine(0);" +
|
|
"var $$notToRemove;" +
|
|
"function a(){$$testInstrument(0);b}");
|
|
|
|
// Test object literal declarations
|
|
test(kPreamble + "var a = { b: function(){c} }",
|
|
initCode +
|
|
"var $$notToRemove;" +
|
|
"$$testDefine(0);" +
|
|
"var a = { b: function(){$$testInstrument(0);c} }");
|
|
|
|
// Test multiple object literal declarations
|
|
test(kPreamble +
|
|
"var a = { b: function(){c}, d: function(){e} }",
|
|
initCode +
|
|
"var $$notToRemove;" +
|
|
"$$testDefine(0);" +
|
|
"$$testDefine(1);" +
|
|
"var a={b:function(){$$testInstrument(0);c}," +
|
|
"d:function(){$$testInstrument(1);e}}");
|
|
|
|
// Test recursive object literal declarations
|
|
test(kPreamble +
|
|
"var a = { b: { f: function(){c} }, d: function(){e} }",
|
|
initCode +
|
|
"var $$notToRemove;" +
|
|
"$$testDefine(0);" +
|
|
"$$testDefine(1);" +
|
|
"var a={b:{f:function(){$$testInstrument(0);c}}," +
|
|
"d:function(){$$testInstrument(1);e}}");
|
|
}
|
|
|
|
public void testEmpty() {
|
|
this.instrumentationPb = "";
|
|
test("function a(){b}", "function a(){b}");
|
|
}
|
|
|
|
public void testAppNameSetter() {
|
|
this.instrumentationPb = "app_name_setter: \"setAppName\"";
|
|
test("function a(){b}", "setAppName(\"testfile.js\");function a(){b}");
|
|
}
|
|
|
|
public void testInit() {
|
|
this.instrumentationPb = "init: \"var foo = 0;\"\n" +
|
|
"init: \"function f(){g();}\"\n";
|
|
test("function a(){b}",
|
|
"var foo = 0;function f(){g()}function a(){b}");
|
|
}
|
|
|
|
public void testDeclare() {
|
|
this.instrumentationPb = "report_defined: \"$$testDefine\"";
|
|
test("function a(){b}", "$$testDefine(0);function a(){b}");
|
|
}
|
|
|
|
public void testCall() {
|
|
this.instrumentationPb = "report_call: \"$$testCall\"";
|
|
test("function a(){b}", "function a(){$$testCall(0);b}");
|
|
}
|
|
|
|
public void testNested() {
|
|
this.instrumentationPb = "report_call: \"$$testCall\"\n" +
|
|
"report_defined: \"$$testDefine\"";
|
|
test("function a(){ function b(){}}",
|
|
"$$testDefine(1);$$testDefine(0);" +
|
|
"function a(){$$testCall(1);function b(){$$testCall(0)}}");
|
|
}
|
|
|
|
public void testExitPaths() {
|
|
this.instrumentationPb = "report_exit: \"$$testExit\"";
|
|
test("function a(){return}",
|
|
"function a(){return $$testExit(0)}");
|
|
|
|
test("function b(){return 5}",
|
|
"function b(){return $$testExit(0, 5)}");
|
|
|
|
test("function a(){if(2 != 3){return}else{return 5}}",
|
|
"function a(){if(2!=3){return $$testExit(0)}" +
|
|
"else{return $$testExit(0,5)}}");
|
|
|
|
test("function a(){if(2 != 3){return}else{return 5}}b()",
|
|
"function a(){if(2!=3){return $$testExit(0)}" +
|
|
"else{return $$testExit(0,5)}}b()");
|
|
|
|
test("function a(){if(2 != 3){return}else{return 5}}",
|
|
"function a(){if(2!=3){return $$testExit(0)}" +
|
|
"else{return $$testExit(0,5)}}");
|
|
}
|
|
|
|
public void testExitNoReturn() {
|
|
this.instrumentationPb = "report_exit: \"$$testExit\"";
|
|
test("function a(){}",
|
|
"function a(){$$testExit(0);}");
|
|
|
|
test("function a(){b()}",
|
|
"function a(){b();$$testExit(0);}");
|
|
}
|
|
|
|
public void testPartialExitPaths() {
|
|
this.instrumentationPb = "report_exit: \"$$testExit\"";
|
|
test("function a(){if (2 != 3) {return}}",
|
|
"function a(){if (2 != 3){return $$testExit(0)}$$testExit(0)}");
|
|
}
|
|
|
|
public void testExitTry() {
|
|
this.instrumentationPb = "report_exit: \"$$testExit\"";
|
|
test("function a(){try{return}catch(err){}}",
|
|
"function a(){try{return $$testExit(0)}catch(err){}$$testExit(0)}");
|
|
|
|
test("function a(){try{}catch(err){return}}",
|
|
"function a(){try{}catch(err){return $$testExit(0)}$$testExit(0)}");
|
|
|
|
test("function a(){try{return}finally{}}",
|
|
"function a(){try{return $$testExit(0)}finally{}$$testExit(0)}");
|
|
|
|
test("function a(){try{return}catch(err){}finally{}}",
|
|
"function a(){try{return $$testExit(0)}catch(err){}finally{}" +
|
|
"$$testExit(0)}");
|
|
|
|
test("function a(){try{return 1}catch(err){return 2}}",
|
|
"function a(){try{return $$testExit(0, 1)}" +
|
|
"catch(err){return $$testExit(0,2)}}");
|
|
|
|
test("function a(){try{return 1}catch(err){return 2}finally{}}",
|
|
"function a(){try{return $$testExit(0, 1)}" +
|
|
"catch(err){return $$testExit(0,2)}" +
|
|
"finally{}$$testExit(0)}");
|
|
|
|
test("function a(){try{return 1}catch(err){return 2}finally{return}}",
|
|
"function a(){try{return $$testExit(0, 1)}" +
|
|
"catch(err){return $$testExit(0,2)}finally{return $$testExit(0)}}");
|
|
|
|
test("function a(){try{}catch(err){}finally{return}}",
|
|
"function a(){try{}catch(err){}finally{return $$testExit(0)}}");
|
|
}
|
|
|
|
public void testNestedExit() {
|
|
this.instrumentationPb = "report_exit: \"$$testExit\"\n" +
|
|
"report_defined: \"$$testDefine\"";
|
|
test("function a(){ return function(){ return c;}}",
|
|
"$$testDefine(1);function a(){$$testDefine(0);" +
|
|
"return $$testExit(1, function(){return $$testExit(0, c);});}");
|
|
}
|
|
|
|
public void testProtobuffParseFail() {
|
|
this.instrumentationPb = "not an ascii pb\n";
|
|
test("function a(){b}", "", RhinoErrorReporter.PARSE_ERROR);
|
|
}
|
|
|
|
public void testInitJsParseFail() {
|
|
this.instrumentationPb = "init: \"= assignWithNoLhs();\"";
|
|
test("function a(){b}", "", RhinoErrorReporter.PARSE_ERROR);
|
|
}
|
|
|
|
private class NameAndInstrumentFunctions implements CompilerPass {
|
|
private final Compiler compiler;
|
|
NameAndInstrumentFunctions(Compiler compiler) {
|
|
this.compiler = compiler;
|
|
}
|
|
|
|
@Override
|
|
public void process(Node externs, Node root) {
|
|
FunctionNames functionNames = new FunctionNames(compiler);
|
|
functionNames.process(externs, root);
|
|
|
|
InstrumentFunctions instrumentation =
|
|
new InstrumentFunctions(compiler, functionNames,
|
|
"test init code", "testfile.js",
|
|
new StringReader(instrumentationPb));
|
|
instrumentation.process(externs, root);
|
|
}
|
|
}
|
|
}
|