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}