| /******************************************************************************* |
| * 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; |
| } |
| } |