/*
* The Apache Software License, Version 1.1
*
* Copyright (C) 2000-2002 The Apache Software Foundation. All rights
* reserved.
* Copyright (C) 2010 John Lewis
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "Ant" and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package net.sourceforge.cobertura.test;
import groovy.util.AntBuilder;
import groovy.util.Node;
import net.sourceforge.cobertura.ant.ReportTask;
import net.sourceforge.cobertura.test.util.TestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.types.DirSet;
import org.apache.tools.ant.types.Path;
import org.junit.Test;
import java.io.File;
import java.util.HashMap;
import static org.junit.Assert.assertTrue;
public class IgnoreTrivialFunctionalTest extends AbstractCoberturaTestCase {
AntBuilder ant = TestUtils.getCoberturaAntBuilder(TestUtils
.getCoberturaClassDir());
TestUtils testUtil = new TestUtils();
Node dom;
IgnoreUtil ignoreUtil;
@Test
public void ignoreTrivialTest() throws Exception {
/*
* Use a temporary directory and create a Main.java source file that
* has trivial methods such as .
*/
File tempDir = TestUtils.getTempDir();
File srcDir = new File(tempDir, "src");
File reportDir = new File(tempDir, "report");
File instrumentDir = new File(tempDir, "instrument");
instrumentDir.mkdirs();
File mainSourceFile = new File(srcDir, "mypackage/Main.java");
File interfaceSourceFile = new File(srcDir,
"mypackage/MyInterface.java");
File datafile = new File(srcDir, "cobertura.ser");
mainSourceFile.getParentFile().mkdirs();
FileUtils.write(interfaceSourceFile, "\n package mypackage;" + "\n "
+ "\n public interface MyInterface {"
+ "\n public void myInterfaceMethod();" + "\n }");
FileUtils
.write(
mainSourceFile,
"\n package mypackage;"
+ "\n "
+ "\n public class Main extends Thread {"
+ "\n "
+ "\n public static class MyObject implements MyInterface"
+ "\n {"
+ "\n public void myInterfaceMethod()"
+ "\n {"
+ "\n }"
+ "\n }"
+ "\n "
+ "\n public static void main(String[] args) {"
+ "\n Main main = new Main();"
+ "\n "
+ "\n /*"
+ "\n * Call all methods so they will be considered \"covered\" unless"
+ "\n * they are ignored as trivial."
+ "\n *"
+ "\n * These are in no particular order."
+ "\n */"
+ "\n main.getterTrivial();"
+ "\n main.empty();"
+ "\n main.getVoid();"
+ "\n main.getIntWithIntParm(0);"
+ "\n main.isBool();"
+ "\n main.hasBool();"
+ "\n main.set();"
+ "\n main.setInt(1);"
+ "\n main.setIntWithTwoParms(1, 2);"
+ "\n main.getMultiDimArray();"
+ "\n main.setIncrement(1);"
+ "\n main.setConst(\"\");"
+ "\n main.getArray();"
+ "\n main.getObject();"
+ "\n main.getStatic();"
+ "\n main.setStatic(1);"
+ "\n main.setISTORE(1);"
+ "\n main.setLSTORE(1);"
+ "\n main.setFSTORE((float)1.0);"
+ "\n main.setDSTORE(1.0);"
+ "\n main.setASTORE(null);"
+ "\n main.getINVOKEVIRTUAL();"
+ "\n main.getINVOKESPECIAL();"
+ "\n main.getINVOKESTATIC();"
+ "\n main.setINVOKEINTERFACE(new MyObject());"
+ "\n "
+ "\n // call constructors in no particular order"
+ "\n new Main(1);"
+ "\n new Main(true);"
+ "\n new Main(\"str\");"
+ "\n new Main(\"\", \"\");"
+ "\n new Main(\"\", 0);"
+ "\n new Main(\"\", true);"
+ "\n new Main((Thread) null, \"string\");"
+ "\n new Main((Thread) null, 0);"
+ "\n }"
+ "\n "
+ "\n // Be careful when initializing members. If you instantiate an"
+ "\n // object, then trivial constructors will become non-trivial."
+ "\n // Ex. Integer myInteger = new Integer(1); will cause Main() to be non-trivial."
+ "\n int myint;"
+ "\n boolean mybool;"
+ "\n private static int mystatic;"
+ "\n "
+ "\n // trivial constructor"
+ "\n public Main() {"
+ "\n }"
+ "\n "
+ "\n // constructors that just call super() are trivial"
+ "\n public Main(Thread t, String str)"
+ "\n {"
+ "\n super(str);"
+ "\n }"
+ "\n "
+ "\n // constructors that just call super() are usually trivial, but"
+ "\n // this constructor uses a constant, so it is considered non-trivial."
+ "\n public Main(Thread t, int i)"
+ "\n {"
+ "\n super(\"string\");"
+ "\n }"
+ "\n "
+ "\n public Main(boolean bool) {"
+ "\n // non-trivial conditional"
+ "\n myint = bool ? 0 : 1;"
+ "\n }"
+ "\n "
+ "\n public Main(int num) {"
+ "\n // non-trivial switch"
+ "\n switch(num) {"
+ "\n default:"
+ "\n }"
+ "\n }"
+ "\n "
+ "\n public Main(String str) {"
+ "\n // setting of statics is non-trivial"
+ "\n mystatic = 2;"
+ "\n }"
+ "\n "
+ "\n public Main(String str1, String str2)"
+ "\n {"
+ "\n // non-trivial method call"
+ "\n privateMethod();"
+ "\n }"
+ "\n "
+ "\n public Main(String str1, int i)"
+ "\n {"
+ "\n // non-trivial object construction"
+ "\n new StringBuffer();"
+ "\n }"
+ "\n "
+ "\n public Main(String str1, boolean bool)"
+ "\n {"
+ "\n // non-trivial this() call"
+ "\n this(str1, 0);"
+ "\n }"
+ "\n "
+ "\n "
+ "\n // trivial getter"
+ "\n public int getterTrivial() {"
+ "\n return myint;"
+ "\n }"
+ "\n "
+ "\n // trivial getter"
+ "\n public boolean isBool() {"
+ "\n return mybool;"
+ "\n }"
+ "\n "
+ "\n // trivial getter"
+ "\n public boolean hasBool() {"
+ "\n return mybool;"
+ "\n }"
+ "\n "
+ "\n // trivial setter"
+ "\n public void setInt(int i) {"
+ "\n myint = i;"
+ "\n }"
+ "\n "
+ "\n // this would be trivial, but it is a getter that with no return value"
+ "\n public void getVoid() {"
+ "\n }"
+ "\n "
+ "\n // \"empty\" does not start with \"get\", \"is\", \"has\", or \"set\", so"
+ "\n // it is considered non-trivial."
+ "\n private int empty() {"
+ "\n return 0;"
+ "\n }"
+ "\n "
+ "\n // this is a getter that takes a parameter, so it is non-trivial."
+ "\n public int getIntWithIntParm(int i) {"
+ "\n return 0;"
+ "\n }"
+ "\n "
+ "\n // this would be a trivial setter, but it does not have a parameter."
+ "\n public void set() {"
+ "\n }"
+ "\n "
+ "\n // this would be a trivial setter, but it has more than one parameter."
+ "\n public void setIntWithTwoParms(int i, int j) {"
+ "\n myint = i;"
+ "\n }"
+ "\n "
+ "\n public int[][] getMultiDimArray() {"
+ "\n // non-trivial construction of a multi-dimensional array"
+ "\n return new int[1][1];"
+ "\n }"
+ "\n "
+ "\n public void setIncrement(int i) {"
+ "\n // non-trivial increment of local variable"
+ "\n i++;"
+ "\n }"
+ "\n "
+ "\n public void setConst(String str) {"
+ "\n /*"
+ "\n * cause visitLdcInsn to be called because \"str\" is in the"
+ "\n * runtime constant pool. An LDC operation is performed"
+ "\n * which is considered non-trivial."
+ "\n */"
+ "\n System.out.println(\"str\");"
+ "\n }"
+ "\n "
+ "\n public int[] getArray() {"
+ "\n // causes visitIntInsn to be called. Creating an array is a \"single int operand\"."
+ "\n // non-trivial."
+ "\n return new int[1];"
+ "\n }"
+ "\n "
+ "\n public Object getObject() {"
+ "\n // causes visitTypeInsn to be called. Creating an object is a type instruction."
+ "\n // non-trivial."
+ "\n return new Object();"
+ "\n }"
+ "\n "
+ "\n public int getStatic() {"
+ "\n // getting a static is non-trivial."
+ "\n return mystatic;"
+ "\n }"
+ "\n "
+ "\n public void setStatic(int i) {"
+ "\n // setting a static is non-trivial."
+ "\n mystatic = i;"
+ "\n }"
+ "\n "
+ "\n // non-trivial local variable instruction (causes visitVarInsn(ISTORE)) (int store to local var)"
+ "\n public void setISTORE(int i) {"
+ "\n i = 0;"
+ "\n }"
+ "\n "
+ "\n // non-trivial local variable instruction (causes visitVarInsn(LSTORE)) (long store to local var)"
+ "\n public void setLSTORE(long l) {"
+ "\n l = 0;"
+ "\n }"
+ "\n "
+ "\n // non-trivial local variable instruction (causes visitVarInsn(FSTORE)) (floating store to local var)"
+ "\n public void setFSTORE(float f) {"
+ "\n f = 0;"
+ "\n }"
+ "\n "
+ "\n // non-trivial local variable instruction (causes visitVarInsn(DSTORE)) (double store to local var)"
+ "\n public void setDSTORE(double d) {"
+ "\n d = 0;"
+ "\n }"
+ "\n "
+ "\n // non-trivial local variable instruction (causes visitVarInsn(ASTORE)) (object store to local var)"
+ "\n public void setASTORE(Object obj) {"
+ "\n obj = null;"
+ "\n }"
+ "\n "
+ "\n public void publicMethod() {"
+ "\n }"
+ "\n private void privateMethod() {"
+ "\n }"
+ "\n public static void staticMethod() {"
+ "\n }"
+ "\n "
+ "\n // non-trivial public method call (causes visitMethodInsn(INVOKEVIRTUAL))"
+ "\n public int getINVOKEVIRTUAL() {"
+ "\n publicMethod();"
+ "\n return 0;"
+ "\n }"
+ "\n "
+ "\n // non-trivial private method call (causes visitMethodInsn(INVOKESPECIAL)) "
+ "\n public int getINVOKESPECIAL() {"
+ "\n privateMethod();"
+ "\n return 0;"
+ "\n }"
+ "\n "
+ "\n // non-trivial static method call (causes visitMethodInsn(INVOKESTATIC)) "
+ "\n public int getINVOKESTATIC() {"
+ "\n staticMethod();"
+ "\n return 0;"
+ "\n }"
+ "\n "
+ "\n // non-trivial interface method call (causes visitMethodInsn(INVOKEINTERFACE))"
+ "\n public void setINVOKEINTERFACE(MyInterface obj) {"
+ "\n obj.myInterfaceMethod();" + "\n }"
+ "\n }");
TestUtils.compileSource(ant, srcDir);
TestUtils.instrumentClasses(ant, srcDir, datafile, instrumentDir,
new HashMap() {
{
put("ignoretrivial", true);
}
});
/*
* Kick off the Main (instrumented) class.
*/
Path classpath = new Path(TestUtils.project);
DirSet dirSetInstrumentDir = new DirSet();
DirSet dirSetSrcDir = new DirSet();
dirSetInstrumentDir.setDir(instrumentDir);
dirSetSrcDir.setDir(srcDir);
classpath.addDirset(dirSetInstrumentDir);
classpath.addDirset(dirSetSrcDir);
classpath.addDirset(TestUtils.getCoberturaClassDirSet());
Java java = new Java();
java.setProject(TestUtils.project);
java.setClassname("mypackage.Main");
java.setDir(srcDir);
java.setFork(true);
java.setFailonerror(true);
java.setClasspath(classpath);
java.execute();
/*
* Now create a cobertura xml file and make sure the correct counts are in it.
*/
ReportTask reportTask = new ReportTask();
reportTask.setProject(TestUtils.project);
reportTask.setDataFile(datafile.getAbsolutePath());
reportTask.setFormat("xml");
reportTask.setDestDir(srcDir);
reportTask.execute();
dom = TestUtils.getXMLReportDOM(srcDir.getAbsolutePath()
+ "/coverage.xml");
ignoreUtil = new IgnoreUtil("mypackage.Main", dom);
// trivial empty constructor
assertIgnored("<init>", "()V");
// trivial constructor Main(Thread, String) that just calls super()
assertIgnored("<init>", "(Ljava/lang/Thread;Ljava/lang/String;)V");
// trivial getter
assertIgnored("getterTrivial");
// isBool is trivial
assertIgnored("isBool");
// hasBool is trivial
assertIgnored("hasBool");
// setInt is trivial
assertIgnored("setInt");
// Main(int) has non-trivial switch
assertNotIgnored("<init>", "(I)V");
// Main(boolean) has non-trivial conditional
assertNotIgnored("<init>", "(Z)V");
// "empty" does not start with "get", "is", "has", or "set".
assertNotIgnored("empty");
// gets with no return are considered non-trivial
assertNotIgnored("getVoid");
// gets that have parameters are considered non-trivial
assertNotIgnored("getIntWithIntParm");
// sets that have no parameters are considered non-trivial
assertNotIgnored("set");
// sets that have more than one parameters are considered non-trivial
assertNotIgnored("setIntWithTwoParms");
// don't ignore methods with multi-dimensional array creates
assertNotIgnored("getMultiDimArray");
// don't ignore methods with increment instructions for local variables
assertNotIgnored("setIncrement");
// don't ignore methods with LDC instructions (that use constants from the runtime pool)
assertNotIgnored("setConst");
assertNotIgnored("<init>", "(Ljava/lang/Thread;I)V"); // Main(Thread, int)
// don't ignore methods with a single int operand (like creating an array).
assertNotIgnored("getArray");
// don't ignore methods with type instructions (like creating an object).
assertNotIgnored("getObject");
// don't ignore methods that use statics.
assertNotIgnored("getStatic");
assertNotIgnored("setStatic");
assertNotIgnored("<init>", "(Ljava/lang/String;)V");
// non-trivial local variable instructions (causes visitVarInsn call)
assertNotIgnored("setISTORE");
assertNotIgnored("setLSTORE");
assertNotIgnored("setFSTORE");
assertNotIgnored("setDSTORE");
assertNotIgnored("setASTORE");
// non-trivial method calls
assertNotIgnored("getINVOKEVIRTUAL");
assertNotIgnored("getINVOKESPECIAL");
assertNotIgnored("getINVOKESTATIC");
assertNotIgnored("setINVOKEINTERFACE");
assertNotIgnored("<init>", "(Ljava/lang/String;Ljava/lang/String;)V"); // Main(String, String)
assertNotIgnored("<init>", "(Ljava/lang/String;I)V"); // Main(String, int)
assertNotIgnored("<init>", "(Ljava/lang/String;Z)V"); // Main(String, boolean)
/*
* Now create a cobertura html report and make sure the files are created.
*/
reportTask = new ReportTask();
reportTask.setProject(TestUtils.project);
reportTask.setDataFile(datafile.getAbsolutePath());
reportTask.setFormat("html");
reportTask.setDestDir(reportDir);
reportTask.setSrcDir(srcDir.getAbsolutePath());
reportTask.execute();
assertTrue(new File(reportDir, "index.html").exists());
assertTrue(new File(reportDir, "mypackage.Main.html").exists());
assertTrue(new File(reportDir, "mypackage.MyInterface.html").exists());
File frameSummaryFile = new File(reportDir, "frame-summary.html");
assertTrue(frameSummaryFile.exists());
TestUtils.checkFrameSummaryHtmlFile(frameSummaryFile);
}
public void assertIgnored(String methodName, String signature) {
ignoreUtil.assertIgnored(methodName, signature);
}
public void assertIgnored(String methodName) {
assertIgnored(methodName, null);
}
public void assertNotIgnored(String methodName, String signature) {
ignoreUtil.assertNotIgnored(methodName, signature);
}
public void assertNotIgnored(String methodName) {
assertNotIgnored(methodName, null);
}
}