CoverageTask.java

    1/*******************************************************************************
    2 * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
    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        for (final TaskEnhancer enhancer : taskEnhancers) {
   70            if (enhancer.supportsTask(subTaskTypeName)) {
   71                enhancer.enhanceTask(task);
   72                return;
   73            }
   74        }
   75
   76        throw new BuildException(format(
   77                "%s is not a valid child of the coverage task.",
   78                subTaskTypeName));
   79
   80    }
   81
   82    /**
   83     * Executes subtask and performs any required cleanup
   84     */
   85    @Override
   86    public void execute() throws BuildException {
   87        if (childTask == null) {
   88            throw new BuildException(
   89                    "A child task must be supplied for the coverage task");
   90        }
   91        log(format("Enhancing %s with coverage.", childTask.getTaskName()));
   92        childTask.execute();
   93    }
   94
   95    /**
   96     * Basic task enhancer that can handle all 'java like' tasks. That is, tasks
   97     * that have a top level fork attribute and nested jvmargs elements
   98     */
   99    private class JavaLikeTaskEnhancer implements TaskEnhancer {
  100
  101        private final String supportedTaskName;
  102
  103        public JavaLikeTaskEnhancer(final String supportedTaskName) {
  104            this.supportedTaskName = supportedTaskName;
  105        }
  106
  107        public boolean supportsTask(final String taskname) {
  108            return taskname.equals(supportedTaskName);
  109        }
  110
  111        public void enhanceTask(final Task task) {
  112            final RuntimeConfigurable configurableWrapper = task
  113                    .getRuntimeConfigurableWrapper();
  114
  115            final String forkValue = (String) configurableWrapper
  116                    .getAttributeMap().get("fork");
  117
  118            if (forkValue == null || !Project.toBoolean(forkValue)) {
  119                throw new BuildException(
  120                        "Coverage can only be applied on a forked VM");
  121            }
  122
  123            addJvmArgs((UnknownElement) task);
  124            task.maybeConfigure();
  125        }
  126
  127        public void addJvmArgs(final UnknownElement task) {
  128            final JvmArgumentHelper jvmArgumentHelper = new JvmArgumentHelper();
  129            final String agentParam = jvmArgumentHelper
  130                    .createJavaAgentParam(getAgentOptions());
  131
  132            final UnknownElement el = new UnknownElement("jvmarg");
  133            el.setTaskName("jvmarg");
  134            el.setQName("jvmarg");
  135
  136            final RuntimeConfigurable runtimeConfigurableWrapper = el
  137                    .getRuntimeConfigurableWrapper();
  138            runtimeConfigurableWrapper.setAttribute("value", agentParam);
  139
  140            task.getRuntimeConfigurableWrapper().addChild(
  141                    runtimeConfigurableWrapper);
  142
  143            task.addChild(el);
  144        }
  145    }
  146
  147    /**
  148     * The task enhancer is responsible for potentially reconfiguring a task to
  149     * support running with code coverage enabled
  150     */
  151    private interface TaskEnhancer {
  152        /**
  153         * @param taskname
  154         *            Task type to enhance
  155         * @return <code>true</code> iff this enhancer is capable of enhacing
  156         *         the requested task type
  157         */
  158        public boolean supportsTask(String taskname);
  159
  160        /**
  161         * Attempt to enhance the supplied task with coverage information. This
  162         * operation may fail if the task is being executed in the current VM
  163         * 
  164         * @param task
  165         *            Task instance to enhance (usually an
  166         *            {@link UnknownElement})
  167         * @throws BuildException
  168         *             Thrown if this enhancer can handle this type of task, but
  169         *             this instance can not be enhanced for some reason.
  170         */
  171        public void enhanceTask(Task task) throws BuildException;
  172    }
  173}