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}