/*
* Cobertura - http://cobertura.sourceforge.net/
*
* Copyright (C) 2008 John Lewis
* Copyright (C) 2006 Mark Doliner
*
* Note: This file is dual licensed under the GPL and the Apache
* Source License 1.1 (so that it can be used from both the main
* Cobertura classes and the ant tasks).
*
* Cobertura is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Cobertura is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cobertura; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
package net.sourceforge.cobertura.ant;
import net.sourceforge.cobertura.reporting.JUnitXMLHelper;
import net.sourceforge.cobertura.test.AbstractCoberturaTestCase;
import net.sourceforge.cobertura.test.util.TestUtils;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Path.PathElement;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.xpath.XPath;
import org.junit.Test;
import java.io.*;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.*;
/**
* These tests generally exec ant to run a test.xml file. A different target is used for
* each test. The text.xml file sets up a test, instruments, runs junit, and generates a
* coverage xml report. Then the xml report is parsed and checked.
*
* @author jwlewi
*/
public class FunctionalTest extends AbstractCoberturaTestCase {
private static int forkedJVMDebugPort = 0;
private final static File BASEDIR = new File("src/test/resources/",
"examples/functionaltest1");
@Test
public void testInstrumentUsingDirSet() throws Exception {
runTestAntScript("dirset", "test-dirset");
verify("dirset");
}
@Test
public void testInstrumentUsingIncludesAndExcludes() throws Exception {
runTestAntScript("includes-and-excludes", "test-includes-and-excludes");
verify("includes-and-excludes");
}
@Test
public void testInstrumentUsingClassPath() throws Exception {
runTestAntScript("classpath", "test-classpath");
verify("classpath");
}
@Test
public void testInstrumentUsingWar() throws Exception {
runTestAntScript("classpath", "test-war");
verify("war");
}
private static void verify(String testName) throws Exception {
verifyXml(testName);
verifyHtml(testName);
}
private static void verifyXml(String testName) throws Exception {
Document document = getSummaryXmlReportDocument();
verifyOverallComplexity(document);
document = getXmlReportDocument();
verifyOverallComplexity(document);
// Get a list of all classes listed in the XML report
List classesList = getClassElements(document);
assertTrue("Test " + testName
+ ": Did not find any classes listed in the XML report.",
classesList.size() > 0);
// text.xml only instruments the two "A" classes, so make
// sure those are the only classes listed in the XML report.
boolean firstPackageFound = false;
boolean secondPackageFound = false;
for (Iterator iter = classesList.iterator(); iter.hasNext();) {
boolean verify = true;
Element classElement = (Element) iter.next();
String className = classElement.getAttributeValue("name");
if (className.equals("test.first.A")) {
firstPackageFound = true;
} else if (className.equals("test.second.A")) {
secondPackageFound = true;
} else if (className.equals("test.first.RemoteInterface")
|| (className.equals("test.first.RemoteListener"))
|| (className.equals("test.first.RemoteListener_Stub"))) {
//just ignore - it is ok
verify = false;
} else
fail("Test "
+ testName
+ ": Found a class with the name '"
+ className
+ "' in the XML report, but was only expecting either 'test.first.A' or 'test.second.A'.");
if (verify) {
verifyClass(className, testName, classElement);
}
}
assertTrue("Test " + testName
+ ": Did not find class 'test.first.A' in the XML report.",
firstPackageFound);
assertTrue("Test " + testName
+ ": Did not find class 'test.second.A' in the XML report.",
secondPackageFound);
}
private static void verifyOverallComplexity(Document document) {
String complexity = document.getRootElement().getAttributeValue(
"complexity");
assertEquals("Invalid overall complexity ", "1.0", complexity);
}
private static Document getXmlReportDocument() throws IOException,
JDOMException {
File xmlFile = new File(BASEDIR, "reports/cobertura-xml/coverage.xml");
Document document = JUnitXMLHelper.readXmlFile(xmlFile, true);
return document;
}
private static Document getSummaryXmlReportDocument() throws IOException,
JDOMException {
File xmlFile = new File(BASEDIR,
"reports/cobertura-xml/coverage-summary.xml");
Document document = JUnitXMLHelper.readXmlFile(xmlFile, true);
return document;
}
/**
* Use XPath to get all <class> elements in the
* cobertura.xml file under the given directory.
*
* @return A list of JDOM Elements.
*/
private static List getClassElements(Document document) throws IOException,
JDOMException {
XPath xpath = XPath
.newInstance("/coverage/packages/package/classes/class");
List classesList = xpath.selectNodes(document);
return classesList;
}
/**
* Verify that the class's expected methods are found. Look for
* a method called "call" which should have a hit count of 1.
* The method called "dontCall" should have a hit count of 0.
*
* @param testName
*/
private static void verifyClass(String className, String testName,
Element classElement) {
verifyComplexity(className, classElement);
// Get a list of methods
Element methodsElement = classElement.getChild("methods");
List methodList = methodsElement.getChildren("method");
assertTrue("Test " + testName
+ ": Did not find any methods listed in the class "
+ classElement.getAttributeValue("name"), methodList.size() > 0);
boolean callMethodFound = false;
boolean dontCallMethodFound = false;
for (Iterator iter = methodList.iterator(); iter.hasNext();) {
Element methodElement = (Element) iter.next();
String methodName = methodElement.getAttributeValue("name");
if (methodName.equals("call")) {
if (callMethodFound) {
fail("Test "
+ testName
+ ": Found more than one instance of the method 'call' in the class "
+ classElement.getAttributeValue("name"));
}
callMethodFound = true;
verifyMethod(testName, classElement, methodElement, 1);
} else if (methodName.equals("dontCall")) {
if (dontCallMethodFound) {
fail("Test "
+ testName
+ ": Found more than one instance of the method 'dontCall' in the class "
+ classElement.getAttributeValue("name"));
}
dontCallMethodFound = true;
verifyMethod(testName, classElement, methodElement, 0);
} else if (methodName.equals("<init>")
|| methodName.equals("someMethod")) {
// These methods are ok--ignore them.
} else {
fail("Test "
+ testName
+ ": Found method "
+ methodName
+ " in the class "
+ classElement.getAttributeValue("name")
+ ", but was only expecting either 'call' or 'dontCall'.");
}
}
assertTrue("Test " + testName
+ ": Did not find method 'call' in the class "
+ classElement.getAttributeValue("name"), callMethodFound);
assertTrue("Test " + testName
+ ": Did not find method 'dontCall' in the class "
+ classElement.getAttributeValue("name"), dontCallMethodFound);
}
private static void verifyComplexity(String className, Element classElement) {
String complexity = classElement.getAttributeValue("complexity");
assertEquals("Invalid complexity with class " + className, "1.0",
complexity);
}
/**
* Look at all lines in a method and make sure they have hit counts that
* match the expectedHits.
*/
private static void verifyMethod(String testName, Element classElement,
Element methodElement, int expectedHits) {
Element linesElement = methodElement.getChild("lines");
List lineList = linesElement.getChildren("line");
assertTrue("Test " + testName + ", class "
+ classElement.getAttributeValue("name")
+ ": Did not find any lines in the method "
+ methodElement.getAttributeValue("name"), lineList.size() > 0);
for (Iterator iter = lineList.iterator(); iter.hasNext();) {
Element lineElement = (Element) iter.next();
String hitsString = lineElement.getAttributeValue("hits");
int hits = Integer.parseInt(hitsString);
assertEquals("Test " + testName + ", class "
+ classElement.getAttributeValue("name")
+ ": Found incorrect hit count for the method "
+ methodElement.getAttributeValue("name"), expectedHits,
hits);
}
}
private static void verifyHtml(String testName) throws Exception {
File htmlReportDir = new File(BASEDIR, "reports/cobertura-html");
// Get all files from report directory
String htmlFiles[] = htmlReportDir.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".html");
}
});
Arrays.sort(htmlFiles);
assertTrue(htmlFiles.length >= 5);
// Assert that all required files are there
String[] requiredFiles = {"index.html", "help.html",
"frame-packages.html", "frame-summary.html",
"frame-sourcefiles.html", "test.first.A.html"};
for (int i = 0; i < requiredFiles.length; i++) {
if (!containsFile(htmlFiles, requiredFiles[i])) {
fail("Test " + testName + ": File " + requiredFiles[i]
+ " not found among report files");
}
}
// Validate selected files
String previousPrefix = "NONE";
for (int i = 0; i < htmlFiles.length; i++) {
// Validate file if has prefix different than previous one, or is required file
if (containsFile(requiredFiles, htmlFiles[i])
|| !htmlFiles[i].startsWith(previousPrefix)) {
JUnitXMLHelper.readXmlFile(
new File(htmlReportDir, htmlFiles[i]), true);
}
if (htmlFiles[i].length() > 7) {
previousPrefix = htmlFiles[i].substring(0, 7);
} else {
previousPrefix = htmlFiles[i];
}
}
BufferedReader reader = new BufferedReader(new FileReader(new File(
htmlReportDir, "test.first.A.html")));
String line;
boolean foundSomeMethod = false;
while ((line = reader.readLine()) != null) {
if (line.matches(".*someMethod.*")) {
foundSomeMethod = true;
}
}
assertTrue("someMethod not found in test.first.A.html", foundSomeMethod);
}
private static boolean containsFile(String[] files, String fileName) {
for (int i = 0; i < files.length; i++) {
if (files[i].equals(fileName))
return true;
}
return false;
}
/**
* Use the ant 'java' task to run the test.xml
* file and the specified target.
*/
private static void runTestAntScript(String testName, String target)
throws IOException {
Java java = new Java();
java.setProject(TestUtils.project);
java.setTaskName("java");
// Call ant launcher. Requires ant-lancher.jar.
java.setClassname("org.apache.tools.ant.launch.Launcher");
java.setFork(true);
AntUtil.transferCoberturaDataFileProperty(java);
if (forkedJVMDebugPort > 0) {
java.createJvmarg().setValue("-Xdebug");
java.createJvmarg().setValue(
"-Xrunjdwp:transport=dt_socket,address="
+ forkedJVMDebugPort + ",server=y,suspend=y");
}
java.createArg().setValue("-f");
java.createArg().setValue(BASEDIR + "/build.xml");
java.createArg().setValue(target);
java.setFailonerror(true);
File output = Util.createTemporaryTextFile("cobertura-test");
java.setOutput(output);
java.setFailonerror(true);
//Set the classpath to the same classpath as this JVM
Path classpath = new Path(TestUtils.project);
PathElement pathElement = classpath.new PathElement();
pathElement.setPath(System.getProperty("java.class.path"));
classpath.add(TestUtils.getCoberturaDefaultClasspath());
classpath.add(pathElement);
System.err.println(classpath);
java.setClasspath(classpath);
try {
java.executeJava();
} finally {
if (output.exists()) {
// Put the contents of the output file in the exception
System.out.println("\n\n\nOutput from Ant for " + testName
+ " test:\n----------------------------------------\n"
+ Util.getText(output)
+ "----------------------------------------");
output.delete();
}
}
}
}