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}