CoverageTask.java

    1/*******************************************************************************
    2 * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
    3 * All rights reserved. This program and the accompanying materials
    4 * are made available under the terms of the Eclipse Public License v1.0
    5 * which accompanies this distribution, and is available at
    6 * http://www.eclipse.org/legal/epl-v10.html
    7 *
    8 * Contributors:
    9 *    Brock Janiczak - initial API and implementation
   10 *    
   11 *******************************************************************************/
   12package org.jacoco.ant;
   13
   14import static java.lang.String.format;
   15
   16import java.util.ArrayList;
   17import java.util.Collection;
   18
   19import org.apache.tools.ant.BuildException;
   20import org.apache.tools.ant.Project;
   21import org.apache.tools.ant.RuntimeConfigurable;
   22import org.apache.tools.ant.Task;
   23import org.apache.tools.ant.TaskContainer;
   24import org.apache.tools.ant.UnknownElement;
   25
   26/**
   27 * Container task to run Java/Junit tasks with the JaCoCo agent jar. Coverage
   28 * will only be applied if all of the following are true:
   29 * <ul>
   30 * <li>Exactly one sub task may be present</li>
   31 * <li>Task must be either Java or JUnit</li>
   32 * <li>Task must be using a forked VM (so vm args can be passed)</li>
   33 * </ul>
   34 * 
   35 * @author Brock Janiczak
   36 * @version 0.4.1.20101007204400
   37 */
   38public class CoverageTask extends AbstractCoverageTask implements TaskContainer {
   39
   40    private final Collection<TaskEnhancer> taskEnhancers = new ArrayList<TaskEnhancer>();
   41    private Task childTask;
   42
   43    /**
   44     * Creates a new default coverage task
   45     */
   46    public CoverageTask() {
   47        taskEnhancers.add(new JavaLikeTaskEnhancer("java"));
   48        taskEnhancers.add(new JavaLikeTaskEnhancer("junit"));
   49    }
   50
   51    /**
   52     * Add child task to this container and reconfigure it to run with coverage
   53     * enabled
   54     */
   55    public void addTask(final Task task) {
   56        if (childTask != null) {
   57            throw new BuildException(
   58                    "Only one child task can be supplied to the coverge task");
   59        }
   60
   61        this.childTask = task;
   62
   63        final UnknownElement unknownElement = (UnknownElement) task;
   64
   65        final String subTaskTypeName = unknownElement.getTaskType();
   66
   67        final TaskEnhancer enhancer = findEnhancerForTask(subTaskTypeName);
   68        if (enhancer == null) {
   69            throw new BuildException(format(
   70                    "%s is not a valid child of the coverage task.",
   71                    subTaskTypeName));
   72        }
   73
   74        if (isEnabled()) {
   75            log(format("Enhancing %s with coverage.", childTask.getTaskName()));
   76            enhancer.enhanceTask(task);
   77        }
   78
   79        task.maybeConfigure();
   80    }
   81
   82    private TaskEnhancer findEnhancerForTask(final String taskName) {
   83        for (final TaskEnhancer enhancer : taskEnhancers) {
   84            if (enhancer.supportsTask(taskName)) {
   85                return enhancer;
   86            }
   87        }
   88
   89        return null;
   90    }
   91
   92    /**
   93     * Executes subtask and performs any required cleanup
   94     */
   95    @Override
   96    public void execute() throws BuildException {
   97        if (childTask == null) {
   98            throw new BuildException(
   99                    "A child task must be supplied for the coverage task");
  100        }
  101
  102        childTask.execute();
  103    }
  104
  105    /**
  106     * Basic task enhancer that can handle all 'java like' tasks. That is, tasks
  107     * that have a top level fork attribute and nested jvmargs elements
  108     */
  109    private class JavaLikeTaskEnhancer implements TaskEnhancer {
  110
  111        private final String supportedTaskName;
  112
  113        public JavaLikeTaskEnhancer(final String supportedTaskName) {
  114            this.supportedTaskName = supportedTaskName;
  115        }
  116
  117        public boolean supportsTask(final String taskname) {
  118            return taskname.equals(supportedTaskName);
  119        }
  120
  121        public void enhanceTask(final Task task) {
  122            final RuntimeConfigurable configurableWrapper = task
  123                    .getRuntimeConfigurableWrapper();
  124
  125            final String forkValue = (String) configurableWrapper
  126                    .getAttributeMap().get("fork");
  127
  128            if (forkValue == null || !Project.toBoolean(forkValue)) {
  129                throw new BuildException(
  130                        "Coverage can only be applied on a forked VM");
  131            }
  132
  133            addJvmArgs((UnknownElement) task);
  134        }
  135
  136        public void addJvmArgs(final UnknownElement task) {
  137            final UnknownElement el = new UnknownElement("jvmarg");
  138            el.setTaskName("jvmarg");
  139            el.setQName("jvmarg");
  140
  141            final RuntimeConfigurable runtimeConfigurableWrapper = el
  142                    .getRuntimeConfigurableWrapper();
  143            runtimeConfigurableWrapper.setAttribute("value",
  144                    getLaunchingArgument());
  145
  146            task.getRuntimeConfigurableWrapper().addChild(
  147                    runtimeConfigurableWrapper);
  148
  149            task.addChild(el);
  150        }
  151    }
  152
  153    /**
  154     * The task enhancer is responsible for potentially reconfiguring a task to
  155     * support running with code coverage enabled
  156     */
  157    private interface TaskEnhancer {
  158        /**
  159         * @param taskname
  160         *            Task type to enhance
  161         * @return <code>true</code> iff this enhancer is capable of enhacing
  162         *         the requested task type
  163         */
  164        public boolean supportsTask(String taskname);
  165
  166        /**
  167         * Attempt to enhance the supplied task with coverage information. This
  168         * operation may fail if the task is being executed in the current VM
  169         * 
  170         * @param task
  171         *            Task instance to enhance (usually an
  172         *            {@link UnknownElement})
  173         * @throws BuildException
  174         *             Thrown if this enhancer can handle this type of task, but
  175         *             this instance can not be enhanced for some reason.
  176         */
  177        public void enhanceTask(Task task) throws BuildException;
  178    }
  179}