/*
* The Apache Software License, Version 1.1
*
* Copyright (C) 2000-2002 The Apache Software Foundation. All rights
* reserved.
* Copyright (C) 2009 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.util;
import static org.junit.Assert.*;
import groovy.lang.Closure;
import net.sourceforge.cobertura.ant.InstrumentTask;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.tools.ant.taskdefs.*;
import org.apache.tools.ant.types.Environment.Variable;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.ZipFileSet;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class WebappServer {
public static final String SIMPLE_SERVLET_CLASSNAME = "com.acme.servlet.SimpleServlet";
private static final String JETTY_DIR = "src/test/resources/jetty";
public static final String SIMPLE_SERVLET_TEXT = "\n package com.acme.servlet;"
+ "\n "
+ "\n import javax.servlet.http.HttpServlet;"
+ "\n import javax.servlet.http.HttpServletRequest;"
+ "\n import javax.servlet.http.HttpServletResponse;"
+ "\n "
+ "\n import java.io.PrintWriter;"
+ "\n import java.io.IOException;"
+ "\n "
+ "\n public class SimpleServlet extends HttpServlet"
+ "\n {"
+ "\n protected void doGet(HttpServletRequest req, HttpServletResponse response) throws IOException"
+ "\n {"
+ "\n response.setContentType(\"text/html;charset=UTF-8\");"
+ "\n PrintWriter out = response.getWriter();"
+ "\n try"
+ "\n {"
+ "\n out.println(\"Hi\");"
+ "\n }"
+ "\n finally"
+ "\n {"
+ "\n if (out != null)"
+ "\n {"
+ "\n out.close();" + "\n }" + "\n }" + "\n }" + "\n }";
public static final String SIMPLE_SERVLET_WEB_XML_TEXT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "\n <web-app version=\"2.4\" xmlns=\"http://java.sun.com/xml/ns/j2ee\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd\">"
+ "\n <servlet>"
+ "\n <servlet-name>SimpleServlet</servlet-name>"
+ "\n <servlet-class>com.acme.servlet.SimpleServlet</servlet-class>"
+ "\n </servlet>"
+ "\n <servlet-mapping>"
+ "\n <servlet-name>SimpleServlet</servlet-name>"
+ "\n <url-pattern>/SimpleServlet</url-pattern>"
+ "\n </servlet-mapping>" + "\n </web-app>";
private static final String LOCALHOST = "127.0.0.1";
private static final String WEB_XML = "src/main/java/net/sourceforge/cobertura/webapp/web.xml";
private static final String SRC_DIR = "src/main/java";
private static final String appName = "simple";
private int webappPort;
private int stopPort;
File dir = new File(TestUtils.getTempDir(), "webserver");
boolean modifyMainCoberturaDataFile;
boolean tomcat;
public WebappServer(File webappServerDir, boolean tomcat) {
this.tomcat = tomcat;
ServerSocket s;
try {
s = new ServerSocket(0);
webappPort = s.getLocalPort();
s.close();
s = new ServerSocket(0);
stopPort = s.getLocalPort();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void deployApp(File srcDir, boolean instrumentCobertura,
boolean deployCoberturaFlush, boolean modifyMainCoberturaDataFile,
String instrumentRegEx) throws Exception {
this.modifyMainCoberturaDataFile = modifyMainCoberturaDataFile;
File extractedDir = new File(dir, "extracted");
File webInfDir = new File(extractedDir, "WEB-INF");
File classesDir = new File(webInfDir, "classes");
File webInfFile = writeWebInfFile(webInfDir,
SIMPLE_SERVLET_WEB_XML_TEXT);
compileSourceFiles(srcDir, classesDir);
copyJettyFiles(dir);
File war = makeWarFile(appName, webInfFile, classesDir);
FileUtils.deleteDirectory(extractedDir);
File coberturaJar = createCoberturaJar();
if (instrumentRegEx != null) {
instrumentWar(war, instrumentRegEx);
}
if (instrumentCobertura != false) {
instrumentCoberturaJar(coberturaJar);
}
if (deployCoberturaFlush != false) {
deployCoberturaFlush(deployCoberturaFlush);
}
}
public void deployApp(File srcDir, boolean instrumentCobertura,
boolean deployCoberturaFlush, boolean modifyMainCoberturaDataFile)
throws Exception {
deployApp(srcDir, instrumentCobertura, deployCoberturaFlush,
modifyMainCoberturaDataFile, "");
}
public void deployApp(File srcDir, boolean deployCoberturaFlush,
String instrumentRegEx) throws Exception {
deployApp(srcDir, false, deployCoberturaFlush, false, instrumentRegEx);
}
public void deployApp(File srcDir, String instrumentRegEx) throws Exception {
deployApp(srcDir, false, instrumentRegEx);
}
public void deployApp(File srcDir) throws Exception {
deployApp(srcDir, "");
}
private File writeWebInfFile(File webInfDir, String text)
throws IOException {
assertTrue(webInfDir.mkdirs());
File webInfFile = new File(webInfDir, "web.xml");
FileUtils.writeStringToFile(webInfFile, text);
return webInfFile;
}
private void compileSourceFiles(File srcDir, File classesDir) {
Mkdir mkdir = new Mkdir();
mkdir.setProject(TestUtils.project);
mkdir.setDir(classesDir);
mkdir.execute();
Javac javac = new Javac();
javac.setProject(TestUtils.project);
javac.setSrcdir(new Path(TestUtils.project, srcDir.getAbsolutePath()));
javac.setDestdir(classesDir);
javac.setDebug(true);
javac.setTarget("1.7");
Path classpath = new Path(TestUtils.project);
FileSet fileSet = new FileSet();
fileSet.setDir(new File(JETTY_DIR));
fileSet.setIncludes("**/*.jar");
classpath.addFileset(fileSet);
javac.setClasspath(classpath);
javac.execute();
}
private void copyJettyFiles(File todir) {
Copy copy = new Copy();
copy.setProject(TestUtils.project);
copy.setTodir(todir);
FileSet fileSet = new FileSet();
fileSet.setProject(TestUtils.project);
fileSet.setDir(new File(JETTY_DIR));
copy.addFileset(fileSet);
copy.execute();
}
private File makeWarFile(String appName, File webInfFile, File classesDir) {
File webappsDir = new File(dir, "webapps");
File war = new File(webappsDir, appName + ".war");
War antWar = new War();
antWar.setProject(TestUtils.project);
antWar.setDestFile(war);
antWar.setWebxml(webInfFile);
ZipFileSet zipFileSet = new ZipFileSet();
zipFileSet.setDir(classesDir);
antWar.addClasses(zipFileSet);
antWar.execute();
return war;
}
private void deployCoberturaFlush(boolean instrumentCobertura)
throws Exception {
File webappsDir = new File(dir, "webapps");
File war = new File(webappsDir, "coberturaFlush.war");
File classesDir = new File("target/build/warClasses");
if (!classesDir.exists())
classesDir.mkdirs();
Javac javac = new Javac();
javac.setProject(TestUtils.project);
javac.setSrcdir(new Path(TestUtils.project, SRC_DIR));
javac.setDestdir(classesDir);
javac.setDebug(true);
Path classpath = new Path(TestUtils.project);
FileSet jettyFileSet = new FileSet();
jettyFileSet.setDir(new File(JETTY_DIR));
jettyFileSet.setIncludes("**/*.jar");
classpath.addFileset(jettyFileSet);
javac.setIncludes("**/FlushCoberturaServlet.java");
javac.setClasspath(classpath);
javac.execute();
War antWar = new War();
antWar.setProject(TestUtils.project);
antWar.setDestFile(war);
antWar.setWebxml(new File(WEB_XML));
ZipFileSet classesFileSet = new ZipFileSet();
classesFileSet.setDir(classesDir);
antWar.addClasses(classesFileSet);
antWar.execute();
if (instrumentCobertura) {
instrumentWar(war, "net.sourceforge.cobertura.*");
}
}
private void instrumentWar(File war, String instrumentRegEx) {
InstrumentTask instrumentTask = new InstrumentTask();
instrumentTask.setProject(TestUtils.project);
instrumentTask.setDataFile(dir.getAbsolutePath() + "/cobertura.ser");
instrumentTask.createIncludeClasses().setRegex(instrumentRegEx);
instrumentTask.createExcludeClasses().setRegex(".*Test.*");
System.out.println(war.getAbsolutePath());
FileSet fileSet = new FileSet();
fileSet.setDir(war.getParentFile());
fileSet.setIncludes("**/*.war");
instrumentTask.addFileset(fileSet);
instrumentTask.execute();
}
private void instrumentCoberturaJar(File coberturaJar) {
InstrumentTask instrumentTask = new InstrumentTask();
instrumentTask.setProject(TestUtils.project);
instrumentTask.setDataFile(dir.getAbsolutePath() + "/cobertura.ser");
instrumentTask.createIncludeClasses().setRegex(
"net.sourceforge.cobertura.*");
instrumentTask.createExcludeClasses().setRegex(".*Test.*");
FileSet fileSet = new FileSet();
fileSet.setDir(new File(coberturaJar.getParent()));
fileSet.setIncludes(coberturaJar.getName());
instrumentTask.addFileset(fileSet);
instrumentTask.execute();
}
/**
* Starts the web server and calls the closure. The web server will be stopped
* when the closure exits (successfully or not).
* @throws Exception
*
*/
public void withRunningServer() throws Exception {
startWebServer();
Map<String, Object> data = new HashMap<String, Object>();
try {
data.put("xmlReport", new File(dir.getAbsolutePath()
+ "/coverage.xml"));
data.put("hostname", LOCALHOST);
data.put("webappPort", webappPort);
data.put("coberturaAnt", TestUtils.getCoberturaAntBuilder(TestUtils
.getTempDir()));
data.put("datafile", getDatafileToUse());
/*
* add a closure that can be called to flush the cobertura data
*/
Closure closure = new Closure(null) {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unused")
public void doCall(HashMap<String, Object> values) {
}
};
System.err.println(closure.call(data));
} finally {
}
}
public void killServer() {
stopWebServer();
}
private File getDatafileToUse() {
File datafile = new File(dir.getAbsolutePath() + "/cobertura.ser");
if (modifyMainCoberturaDataFile) {
/*
* modify the cobertura.ser file created by the instrumentation target
* of the main build.xml file. That way, the counts
* will end up in the overall coverage report
*/
File tempfile = new File("target/build/cobertura.ser");
if (tempfile.exists()) {
datafile = tempfile;
} else {
/*
* It looks like the main build.xml's instrument target has not been run.
* So, a coverage report is not being generated.
*/
}
}
return datafile;
}
private void startWebServer() {
System.out.println("Starting Jetty webapp server on " + webappPort);
Thread t = new Thread() {
@Override
public void run() {
Java java = new Java();
java.setProject(TestUtils.project);
java.setDir(dir);
java.setJar(new File(dir, "start.jar"));
java.setFork(true);
Variable jettyPort = new Variable();
jettyPort.setKey("jetty.port");
jettyPort.setValue(String.valueOf(webappPort));
java.addSysproperty(jettyPort);
Variable stopPortVariable = new Variable();
stopPortVariable.setKey("STOP.PORT");
stopPortVariable.setValue(String.valueOf(stopPort));
java.addSysproperty(stopPortVariable);
Variable stopKey = new Variable();
stopKey.setKey("STOP.KEY");
stopKey.setValue("cobertura");
java.addSysproperty(stopKey);
if (tomcat) {
Variable catalinaHome = new Variable();
catalinaHome.setKey("catalina.home");
catalinaHome.setValue(dir.getAbsolutePath());
java.addSysproperty(catalinaHome);
}
if (modifyMainCoberturaDataFile) {
Variable dataFile = new Variable();
dataFile.setKey("net.sourceforge.cobertura.datafile");
dataFile.setValue(getDatafileToUse().getAbsolutePath());
java.addSysproperty(dataFile);
}
java.execute();
}
};
t.start();
try {
Thread.sleep(10 * 1000); // Sleep for 10 seconds. The servlet needs a few seconds to boot up.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void stopWebServer() {
Thread t = new Thread() {
@Override
public void run() {
Java java = new Java();
java.setProject(TestUtils.project);
java.setJar(new File(TestUtils.getTempDir(),
"webserver/start.jar"));
java.setDir(dir);
java.setFork(true);
java.setArgs("--stop");
Variable stopPortVariable = new Variable();
Variable stopKeyVariable = new Variable();
stopPortVariable.setKey("STOP.PORT");
stopPortVariable.setValue(String.valueOf(stopPort));
stopKeyVariable.setKey("STOP.KEY");
stopKeyVariable.setValue("cobertura");
java.addSysproperty(stopPortVariable);
java.addSysproperty(stopKeyVariable);
java.execute();
java.execute();
}
};
t.start();
}
private File createCoberturaJar() {
File coberturaJar = new File(dir, "lib/cobertura.jar");
File coberturaClassDir = TestUtils.getCoberturaClassDir();
Zip zip = new Zip();
zip.setProject(TestUtils.project);
zip.setDestFile(coberturaJar);
FileSet fileSet = new FileSet();
fileSet.setDir(coberturaClassDir);
zip.addFileset(fileSet);
zip.execute();
return coberturaJar;
}
public static void writeSimpleServletSource(File srcDir) throws IOException {
File servletSourceFile = new File(srcDir,
"com/acme/servlet/SimpleServlet.java");
assertTrue(servletSourceFile.getParentFile().mkdirs());
FileUtils.write(servletSourceFile, SIMPLE_SERVLET_TEXT);
}
public void pingServer() {
String webappResponse = null;
try {
webappResponse = IOUtils.toString(new URL("http://localhost:"
+ webappPort + "/" + appName + "/SimpleServlet")
.openConnection().getInputStream());
} catch (MalformedURLException e) {
e.printStackTrace();
fail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
fail(e.getMessage());
}
assertNotNull(webappResponse);
assertEquals("Webapp response was incorrect", "Hi", webappResponse
.trim());
}
public void pingCoberturaServer() {
//flush the cobertura data by doing an HTTP get
String flushing = null;
try {
flushing = IOUtils.toString(new java.net.URL("http://localhost:"
+ webappPort + "/coberturaFlush/flushCobertura")
.openConnection().getInputStream());
} catch (MalformedURLException e) {
e.printStackTrace();
fail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
fail(e.getMessage());
}
assertNotNull(flushing);
assertEquals("", flushing.trim());
}
public File getXmlReport() {
return new File(dir.getAbsolutePath(), "coverage.xml");
}
}