blob: 9cc9f2b5593a9fc5e83e7ff7d083203daeb3697d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Evgeny Mandrikov - TestNG support
* Brock Janiczak - initial API and implementation
*
*******************************************************************************/
package org.jacoco.ant;
import static java.lang.String.format;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.RuntimeConfigurable;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskContainer;
import org.apache.tools.ant.UnknownElement;
/**
* Container task to run Java/JUnit tasks with the JaCoCo agent jar. Coverage
* will only be applied if all of the following are true:
* <ul>
* <li>Exactly one sub task may be present</li>
* <li>Task must be either Java or JUnit</li>
* <li>Task must be using a forked VM (so vm args can be passed)</li>
* </ul>
*/
public class CoverageTask extends AbstractCoverageTask implements TaskContainer {
private final Collection<TaskEnhancer> taskEnhancers = new ArrayList<TaskEnhancer>();
private Task childTask;
/**
* Creates a new default coverage task
*/
public CoverageTask() {
super();
taskEnhancers.add(new JavaLikeTaskEnhancer("java"));
taskEnhancers.add(new JavaLikeTaskEnhancer("junit"));
taskEnhancers.add(new TestNGTaskEnhancer("testng"));
}
/**
* Add child task to this container and reconfigure it to run with coverage
* enabled
*/
public void addTask(final Task task) {
if (childTask != null) {
throw new BuildException(
"Only one child task can be supplied to the coverge task",
getLocation());
}
this.childTask = task;
final String subTaskTypeName = task.getTaskType();
final TaskEnhancer enhancer = findEnhancerForTask(subTaskTypeName);
if (enhancer == null) {
throw new BuildException(format(
"%s is not a valid child of the coverage task",
subTaskTypeName), getLocation());
}
if (isEnabled()) {
log(format("Enhancing %s with coverage", childTask.getTaskName()));
enhancer.enhanceTask(task);
}
task.maybeConfigure();
}
private TaskEnhancer findEnhancerForTask(final String taskName) {
for (final TaskEnhancer enhancer : taskEnhancers) {
if (enhancer.supportsTask(taskName)) {
return enhancer;
}
}
return null;
}
/**
* Executes subtask and performs any required cleanup
*/
@Override
public void execute() throws BuildException {
if (childTask == null) {
throw new BuildException(
"A child task must be supplied for the coverage task",
getLocation());
}
childTask.execute();
}
/**
* Task enhancer for TestNG. TestNG task always run in a forked VM and has
* nested jvmargs elements
*/
private class TestNGTaskEnhancer extends JavaLikeTaskEnhancer {
public TestNGTaskEnhancer(final String supportedTaskName) {
super(supportedTaskName);
}
@Override
public void enhanceTask(final Task task) {
addJvmArgs(task);
}
}
/**
* Basic task enhancer that can handle all 'java like' tasks. That is, tasks
* that have a top level fork attribute and nested jvmargs elements
*/
private class JavaLikeTaskEnhancer implements TaskEnhancer {
private final String supportedTaskName;
public JavaLikeTaskEnhancer(final String supportedTaskName) {
this.supportedTaskName = supportedTaskName;
}
public boolean supportsTask(final String taskname) {
return taskname.equals(supportedTaskName);
}
public void enhanceTask(final Task task) {
final RuntimeConfigurable configurableWrapper = task
.getRuntimeConfigurableWrapper();
final String forkValue = getProject().replaceProperties(
(String) configurableWrapper.getAttributeMap().get("fork"));
if (!Project.toBoolean(forkValue)) {
throw new BuildException(
"Coverage can only be applied on a forked VM",
getLocation());
}
addJvmArgs(task);
}
public void addJvmArgs(final Task task) {
final UnknownElement el = new UnknownElement("jvmarg");
el.setTaskName("jvmarg");
el.setQName("jvmarg");
final RuntimeConfigurable runtimeConfigurableWrapper = el
.getRuntimeConfigurableWrapper();
runtimeConfigurableWrapper.setAttribute("value",
getLaunchingArgument());
task.getRuntimeConfigurableWrapper().addChild(
runtimeConfigurableWrapper);
((UnknownElement) task).addChild(el);
}
}
/**
* The task enhancer is responsible for potentially reconfiguring a task to
* support running with code coverage enabled
*/
private interface TaskEnhancer {
/**
* @param taskname
* Task type to enhance
* @return <code>true</code> if this enhancer is capable of enhancing
* the requested task type
*/
public boolean supportsTask(String taskname);
/**
* Attempt to enhance the supplied task with coverage information. This
* operation may fail if the task is being executed in the current VM
*
* @param task
* Task instance to enhance (usually an
* {@link UnknownElement})
* @throws BuildException
* Thrown if this enhancer can handle this type of task, but
* this instance can not be enhanced for some reason.
*/
public void enhanceTask(Task task) throws BuildException;
}
}