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