Skip to content

Commit

Permalink
Add tests for CoverageInstrumentationPass.
Browse files Browse the repository at this point in the history
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=111556170
  • Loading branch information
tbreisacher authored and blickly committed Jan 7, 2016
1 parent 42727bd commit e58d572
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 44 deletions.
Expand Up @@ -33,16 +33,17 @@
class CoverageInstrumentationCallback extends class CoverageInstrumentationCallback extends
NodeTraversal.AbstractPostOrderCallback { NodeTraversal.AbstractPostOrderCallback {


private final AbstractCompiler compiler;
private final Map<String, FileInstrumentationData> instrumentationData; private final Map<String, FileInstrumentationData> instrumentationData;

private final CoverageReach reach;
private CoverageReach reach;


static final String ARRAY_NAME_PREFIX = "JSCompiler_lcov_data_"; static final String ARRAY_NAME_PREFIX = "JSCompiler_lcov_data_";



public CoverageInstrumentationCallback( public CoverageInstrumentationCallback(
AbstractCompiler compiler,
Map<String, FileInstrumentationData> instrumentationData, Map<String, FileInstrumentationData> instrumentationData,
CoverageReach reach) { CoverageReach reach) {
this.compiler = compiler;
this.instrumentationData = instrumentationData; this.instrumentationData = instrumentationData;
this.reach = reach; this.reach = reach;
} }
Expand All @@ -61,8 +62,8 @@ private static String getFileName(NodeTraversal traversal) {
* source filename of the AST node. * source filename of the AST node.
*/ */
private String createArrayName(NodeTraversal traversal) { private String createArrayName(NodeTraversal traversal) {
return ARRAY_NAME_PREFIX + return ARRAY_NAME_PREFIX
CoverageUtil.createIdentifierFromText(getFileName(traversal)); + CoverageUtil.createIdentifierFromText(getFileName(traversal));
} }


/** /**
Expand All @@ -78,27 +79,25 @@ private String createArrayName(NodeTraversal traversal) {
* node is needed * node is needed
* @return an instrumentation node corresponding to the line number * @return an instrumentation node corresponding to the line number
*/ */
private Node newInstrumentationNode(NodeTraversal traversal, int lineNumber) { private Node newInstrumentationNode(NodeTraversal traversal, Node node) {
String fileName = getFileName(traversal); int lineNumber = node.getLineno();
String arrayName = createArrayName(traversal); String arrayName = createArrayName(traversal);


// Create instrumentation Node // Create instrumentation Node
// arr[line] = true; Node getElemNode = IR.getelem(
Node nameNode = IR.name(arrayName); IR.name(arrayName),
Node numNode = IR.number(lineNumber - 1); // Make line number 0-based IR.number(lineNumber - 1)); // Make line number 0-based
Node getElemNode = IR.getelem(nameNode, numNode); Node exprNode = IR.exprResult(IR.assign(getElemNode, IR.trueNode()));
Node trueNode = IR.trueNode();
Node assignNode = IR.assign(getElemNode, trueNode);
Node exprNode = IR.exprResult(assignNode);


// Note line as instrumented // Note line as instrumented
String fileName = getFileName(traversal);
if (!instrumentationData.containsKey(fileName)) { if (!instrumentationData.containsKey(fileName)) {
instrumentationData.put(fileName, instrumentationData.put(fileName,
new FileInstrumentationData(fileName, arrayName)); new FileInstrumentationData(fileName, arrayName));
} }
instrumentationData.get(fileName).setLineAsInstrumented(lineNumber); instrumentationData.get(fileName).setLineAsInstrumented(lineNumber);


return exprNode; return exprNode.useSourceInfoIfMissingFromForTree(node);
} }


/** /**
Expand All @@ -107,17 +106,15 @@ private Node newInstrumentationNode(NodeTraversal traversal, int lineNumber) {
* "var arrayNameUsedInFile = [];" * "var arrayNameUsedInFile = [];"
*/ */
private Node newArrayDeclarationNode(NodeTraversal traversal) { private Node newArrayDeclarationNode(NodeTraversal traversal) {
Node arraylitNode = IR.arraylit(); return IR.var(
Node nameNode = IR.name(createArrayName(traversal)); IR.name(createArrayName(traversal)),
nameNode.addChildToFront(arraylitNode); IR.arraylit());
Node varNode = IR.var(nameNode);
return varNode;
} }


/** /**
* @return a Node containing file specific setup logic. * @return a Node containing file specific setup logic.
*/ */
private Node newHeaderNode(NodeTraversal traversal) { private Node newHeaderNode(NodeTraversal traversal, Node srcref) {
String fileName = getFileName(traversal); String fileName = getFileName(traversal);
String arrayName = createArrayName(traversal); String arrayName = createArrayName(traversal);
FileInstrumentationData data = instrumentationData.get(fileName); FileInstrumentationData data = instrumentationData.get(fileName);
Expand All @@ -139,7 +136,7 @@ private Node newHeaderNode(NodeTraversal traversal) {
IR.getprop( IR.getprop(
IR.name("JSCompiler_lcov_fileNames"), IR.name("JSCompiler_lcov_fileNames"),
IR.string("push")), IR.string("push")),
IR.string(fileName)))); IR.string(fileName)))).useSourceInfoIfMissingFromForTree(srcref);
} }


/** /**
Expand All @@ -153,8 +150,9 @@ public void visit(NodeTraversal traversal, Node node, Node parent) {
if (node.isScript()) { if (node.isScript()) {
String fileName = getFileName(traversal); String fileName = getFileName(traversal);
if (instrumentationData.get(fileName) != null) { if (instrumentationData.get(fileName) != null) {
node.addChildToFront(newHeaderNode(traversal)); node.addChildToFront(newHeaderNode(traversal, node));
} }
compiler.reportCodeChange();
return; return;
} }


Expand All @@ -166,29 +164,33 @@ public void visit(NodeTraversal traversal, Node node, Node parent) {


// Add instrumentation code just before a function block. // Add instrumentation code just before a function block.
// Similarly before other constructs: 'with', 'case', 'default', 'catch' // Similarly before other constructs: 'with', 'case', 'default', 'catch'
if (node.isFunction() || if (node.isFunction()
node.isWith() || || node.isWith()
node.isCase() || || node.isCase()
node.isDefaultCase() || || node.isDefaultCase()
node.isCatch()) { || node.isCatch()) {
Node codeBlock = node.getLastChild(); Node codeBlock = node.getLastChild();
codeBlock.addChildToFront( codeBlock.addChildToFront(
newInstrumentationNode(traversal, node.getLineno())); newInstrumentationNode(traversal, node));
compiler.reportCodeChange();
return; return;
} }


// Add instrumentation code as the first child of a 'try' block. // Add instrumentation code as the first child of a 'try' block.
if (node.isTry()) { if (node.isTry()) {
Node firstChild = node.getFirstChild(); Node firstChild = node.getFirstChild();
firstChild.addChildToFront( firstChild.addChildToFront(
newInstrumentationNode(traversal, node.getLineno())); newInstrumentationNode(traversal, node));
compiler.reportCodeChange();
return; return;
} }


// For any other statement, add instrumentation code just before it. // For any other statement, add instrumentation code just before it.
if (parent != null && NodeUtil.isStatementBlock(parent)) { if (parent != null && NodeUtil.isStatementBlock(parent)) {
parent.addChildBefore( parent.addChildBefore(
newInstrumentationNode(traversal, node.getLineno()), node); newInstrumentationNode(traversal, node),
node);
compiler.reportCodeChange();
return; return;
} }
} }
Expand Down
21 changes: 11 additions & 10 deletions src/com/google/javascript/jscomp/CoverageInstrumentationPass.java
Expand Up @@ -39,10 +39,10 @@ class CoverageInstrumentationPass implements CompilerPass {
private Map<String, FileInstrumentationData> instrumentationData; private Map<String, FileInstrumentationData> instrumentationData;
private CoverageReach reach; private CoverageReach reach;


private static final String JS_INSTRUMENTATION_EXTERNS_CODE = private static final String JS_INSTRUMENTATION_EXTERNS_CODE = ""
"var JSCompiler_lcov_executedLines;\n" + + "var JSCompiler_lcov_executedLines;\n"
"var JSCompiler_lcov_instrumentedLines;\n" + + "var JSCompiler_lcov_instrumentedLines;\n"
"var JSCompiler_lcov_fileNames;\n"; + "var JSCompiler_lcov_fileNames;\n";


public enum CoverageReach { public enum CoverageReach {
ALL, ALL,
Expand All @@ -66,11 +66,11 @@ public CoverageInstrumentationPass(AbstractCompiler compiler,
*/ */
private void addHeaderCode(Node script) { private void addHeaderCode(Node script) {
script.addChildToFront( script.addChildToFront(
createConditionalVarDecl("JSCompiler_lcov_executedLines")); createConditionalVarDecl("JSCompiler_lcov_executedLines", script));
script.addChildToFront( script.addChildToFront(
createConditionalVarDecl("JSCompiler_lcov_instrumentedLines")); createConditionalVarDecl("JSCompiler_lcov_instrumentedLines", script));
script.addChildToFront( script.addChildToFront(
createConditionalVarDecl("JSCompiler_lcov_fileNames")); createConditionalVarDecl("JSCompiler_lcov_fileNames", script));
} }


/** /**
Expand All @@ -89,7 +89,8 @@ private Node getInstrumentationExternsNode() {
public void process(Node externsNode, Node rootNode) { public void process(Node externsNode, Node rootNode) {
if (rootNode.hasChildren()) { if (rootNode.hasChildren()) {
NodeTraversal.traverseEs6(compiler, rootNode, NodeTraversal.traverseEs6(compiler, rootNode,
new CoverageInstrumentationCallback(instrumentationData, reach)); new CoverageInstrumentationCallback(
compiler, instrumentationData, reach));


Node firstScript = rootNode.getFirstChild(); Node firstScript = rootNode.getFirstChild();
Preconditions.checkState(firstScript.isScript()); Preconditions.checkState(firstScript.isScript());
Expand All @@ -99,7 +100,7 @@ public void process(Node externsNode, Node rootNode) {
externsNode.addChildToBack(getInstrumentationExternsNode()); externsNode.addChildToBack(getInstrumentationExternsNode());
} }


private static Node createConditionalVarDecl(String name) { private static Node createConditionalVarDecl(String name, Node srcref) {
Node var = IR.var( Node var = IR.var(
IR.name(name), IR.name(name),
IR.or( IR.or(
Expand All @@ -109,6 +110,6 @@ private static Node createConditionalVarDecl(String name) {
JSDocInfoBuilder builder = new JSDocInfoBuilder(false); JSDocInfoBuilder builder = new JSDocInfoBuilder(false);
builder.recordSuppressions(ImmutableSet.of("duplicate")); builder.recordSuppressions(ImmutableSet.of("duplicate"));
var.setJSDocInfo(builder.build()); var.setJSDocInfo(builder.build());
return var; return var.useSourceInfoIfMissingFromForTree(srcref);
} }
} }
5 changes: 2 additions & 3 deletions src/com/google/javascript/jscomp/FileInstrumentationData.java
Expand Up @@ -82,9 +82,8 @@ String getInstrumentedLinesAsHexString() {
* @param lineNumber the line number which was instrumented * @param lineNumber the line number which was instrumented
*/ */
void setLineAsInstrumented(int lineNumber) { void setLineAsInstrumented(int lineNumber) {
Preconditions.checkArgument(lineNumber > 0, Preconditions.checkArgument(
"Expected non-zero positive integer as line " + lineNumber > 0, "Expected non-zero positive integer as line number: %s", lineNumber);
"number.");


// Map the 1-based line number to 0-based bit position // Map the 1-based line number to 0-based bit position
instrumentedBits.set(lineNumber - 1); instrumentedBits.set(lineNumber - 1);
Expand Down
@@ -0,0 +1,79 @@
/*
* Copyright 2016 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 for {@link CoverageInstrumentationPass}.
*/
public final class CoverageInstrumentationPassTest extends CompilerTestCase {
@Override
protected CompilerPass getProcessor(Compiler compiler) {
return new CoverageInstrumentationPass(compiler, CoverageInstrumentationPass.CoverageReach.ALL);
}

@Override
public void setUp() {
// enableLineNumberCheck(false);
allowExternsChanges(true);
}

public void testFunction() {
test(
"function f() { console.log('hi'); }",
LINE_JOINER.join(
"/** @suppress {duplicate} */",
"var JSCompiler_lcov_fileNames = JSCompiler_lcov_fileNames || [];",
"/** @suppress {duplicate} */",
"var JSCompiler_lcov_instrumentedLines = JSCompiler_lcov_instrumentedLines||[];",
"/** @suppress {duplicate} */",
"var JSCompiler_lcov_executedLines = JSCompiler_lcov_executedLines||[];",
"{",
" var JSCompiler_lcov_data_testcode = [];",
" JSCompiler_lcov_executedLines.push(JSCompiler_lcov_data_testcode);",
" JSCompiler_lcov_instrumentedLines.push('01');",
" JSCompiler_lcov_fileNames.push('testcode');",
"}",
"JSCompiler_lcov_data_testcode[0] = true;",
"/** @suppress {duplicate} */",
"var JSCompiler_lcov_fileNames = JSCompiler_lcov_fileNames||[];",
"JSCompiler_lcov_data_testcode[0] = true;",
"/** @suppress {duplicate} */",
"var JSCompiler_lcov_instrumentedLines = JSCompiler_lcov_instrumentedLines||[];",
"JSCompiler_lcov_data_testcode[0] = true;",
"/** @suppress {duplicate} */",
"var JSCompiler_lcov_executedLines = JSCompiler_lcov_executedLines||[];",
"JSCompiler_lcov_data_testcode[0] = true;",
"{",
" JSCompiler_lcov_data_testcode[0] = true;",
" var JSCompiler_lcov_data_testcode=[];",
" JSCompiler_lcov_data_testcode[0] = true;",
" JSCompiler_lcov_executedLines.push(JSCompiler_lcov_data_testcode);",
" JSCompiler_lcov_data_testcode[0] = true;",
" JSCompiler_lcov_instrumentedLines.push('01');",
" JSCompiler_lcov_data_testcode[0] = true;",
" JSCompiler_lcov_fileNames.push('testcode');",
"}",
"function f() {",
" JSCompiler_lcov_data_testcode[0] = true;",
" JSCompiler_lcov_data_testcode[0] = true;",
" JSCompiler_lcov_data_testcode[0] = true;",
" JSCompiler_lcov_data_testcode[0] = true;",
" JSCompiler_lcov_data_testcode[0] = true;",
" JSCompiler_lcov_data_testcode[0] = true;",
" console.log('hi');",
"}"));
}
}

0 comments on commit e58d572

Please sign in to comment.