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