001: package com.canoo.webtest.reporting;
002:
003: import java.util.ArrayList;
004: import java.util.Collections;
005: import java.util.Date;
006: import java.util.Iterator;
007: import java.util.List;
008: import java.util.Map;
009: import java.util.TreeMap;
010:
011: import org.apache.commons.collections.EnumerationUtils;
012: import org.apache.commons.collections.IteratorUtils;
013: import org.apache.commons.lang.ClassUtils;
014: import org.apache.log4j.Logger;
015: import org.apache.tools.ant.IntrospectionHelper;
016: import org.apache.tools.ant.Location;
017: import org.apache.tools.ant.RuntimeConfigurable;
018: import org.apache.tools.ant.Task;
019:
020: /**
021: * Result holder for the execution of a step (or task) and his children.
022: *
023: * @author Marc Guillemot
024: */
025: public class StepResult {
026: private static final Logger LOG = Logger
027: .getLogger(StepResult.class);
028:
029: private static String getName(final Task task) {
030: final String taskName = task.getTaskName();
031: if (taskName == null) {
032: return task.getTaskType() + " "
033: + ClassUtils.getShortClassName(task.getClass());
034: }
035: return taskName;
036: }
037:
038: private final Map fAttributes = new TreeMap();
039: private final List fChildren = new ArrayList();
040: private Date fEndDate;
041: private List fHtmlParserMessages = Collections.EMPTY_LIST;
042: private Location fLocation;
043: private StepResult fParent;
044: private final Date fStartDate = new Date();
045: private boolean fSuccessfull;
046: private String fTaskDescription;
047:
048: private final String fTaskName;
049:
050: /**
051: * Constructs a result for a non executed task (and his children).
052: * This allows to present information in report
053: * about configured tasks that haven't been executed due to an error in a step before them.
054: *
055: * @param taskWrapper the configurable for the task
056: */
057: protected StepResult(final RuntimeConfigurable taskWrapper) {
058: this (taskWrapper.getElementTag());
059: fAttributes.putAll(taskWrapper.getAttributeMap());
060: fTaskDescription = (String) fAttributes.get("description");
061: LOG.debug("Constructing result for non executed task: "
062: + getTaskName());
063:
064: retrieveNestedText(taskWrapper);
065:
066: // HACK: Perhaps should we look if the element is a task or not
067: if ("table".equals(taskWrapper.getElementTag())) {
068: fEndDate = fStartDate;
069: fSuccessfull = true;
070: }
071:
072: // the task may have children
073: addLostChildren(IteratorUtils.asIterator(taskWrapper
074: .getChildren()));
075: }
076:
077: public StepResult(final String taskName) {
078: fTaskName = taskName;
079: }
080:
081: /**
082: * Retrieves the nested text configured within the task, if any, taking care
083: * to remove "noise" as Ant does
084: *
085: * @param taskWrapper the current task wrapper
086: */
087: protected void retrieveNestedText(
088: final RuntimeConfigurable taskWrapper) {
089: LOG.trace("In retrieveNestedText");
090: final String nestedText = taskWrapper.getText().toString()
091: .trim();
092: final IntrospectionHelper ih = IntrospectionHelper
093: .getHelper(taskWrapper.getClass());
094: if (nestedText.length() > 0 && ih.supportsCharacters()) {
095: LOG.debug(taskWrapper.getElementTag() + " supports text: "
096: + ih.getAddTextMethod());
097: fAttributes.put("nested text", nestedText);
098: }
099: }
100:
101: /**
102: * Builds the result holder for the given task
103: *
104: * @param task the task (probably a {@link org.apache.tools.ant.UnknownElement}
105: */
106: StepResult(final Task task) {
107: this (getName(task));
108:
109: retrieveNestedText(task.getRuntimeConfigurableWrapper());
110:
111: // HACK: use WebtestPropertyHelper instead?
112: final Map attributeMap = task.getRuntimeConfigurableWrapper()
113: .getAttributeMap();
114: for (final Iterator iterator = attributeMap.entrySet()
115: .iterator(); iterator.hasNext();) {
116: final Map.Entry entry = (Map.Entry) iterator.next();
117: fAttributes.put(entry.getKey(), task.getProject()
118: .replaceProperties((String) entry.getValue()));
119: }
120: // fAttributes.putAll(task.getRuntimeConfigurableWrapper().getAttributeMap());
121: fTaskDescription = (String) fAttributes.get("description");
122: fAttributes.remove("description");
123:
124: fLocation = task.getLocation();
125: }
126:
127: /**
128: * Receives notification that properties have been expanded in an attribute
129: */
130: void propertiesExpanded(final String originalValue,
131: final String expanded) {
132: // write expanded values (if any) in place of original values
133: // (would be interesting to have both)
134: for (final Iterator iter = fAttributes.entrySet().iterator(); iter
135: .hasNext();) {
136: final Map.Entry entry = (Map.Entry) iter.next();
137: final String unexpandedValue = (String) entry.getValue();
138: if (originalValue.equals(unexpandedValue)) {
139: LOG.debug("Replacing value with expanded value: "
140: + expanded);
141: entry.setValue(expanded);
142: }
143: }
144: }
145:
146: /**
147: * @param result the child to add
148: */
149: public void addChild(final StepResult result) {
150: if (result == null)
151: throw new IllegalArgumentException("child can't be null");
152:
153: result.fParent = this ;
154: fChildren.add(result);
155: }
156:
157: /**
158: * Adds report information for non executed child tasks
159: *
160: * @param iter the iterator over {@link RuntimeConfigurable} child task
161: */
162: protected void addLostChildren(final Iterator iter) {
163: while (iter.hasNext()) {
164: final RuntimeConfigurable child = (RuntimeConfigurable) iter
165: .next();
166: addChild(new StepResult(child));
167: }
168: }
169:
170: /**
171: * Tries to find the child tasks that haven't been executed and adds them to the current result.
172: *
173: * @param task the failing task
174: */
175: protected void addNotExecutedChildren(final Task task) {
176: final RuntimeConfigurable taskWrapper = task
177: .getRuntimeConfigurableWrapper();
178: // may happens that no wrapper is available when the task has not been created by ant
179: // like for some special WebTest tasks (that should probably disappear)
180: if (taskWrapper == null) {
181: LOG.debug("No wrapper found for task " + task
182: + ", skipping lost children search");
183: return;
184: }
185:
186: final int iNbKnownChildren = getChildren().size();
187:
188: final List children = EnumerationUtils.toList(taskWrapper
189: .getChildren());
190: if (iNbKnownChildren < children.size()) {
191: addLostChildren(children.listIterator(iNbKnownChildren));
192: }
193:
194: }
195:
196: /**
197: * Gets the attributes of the task
198: *
199: * @return the attributes
200: */
201: public Map getAttributes() {
202: return fAttributes;
203: }
204:
205: /**
206: * @return the children.
207: */
208: public List getChildren() {
209: return fChildren;
210: }
211:
212: /**
213: * Gets the duration of the task execution.
214: *
215: * @return the task duration, <code>-1</code> if the task hasn't be
216: * executed
217: */
218: public long getDuration() {
219: if (isCompleted()) {
220: return getEndDate().getTime() - getStartDate().getTime();
221: }
222: return -1;
223: }
224:
225: /**
226: * Gets the date at which the execution of the task finished
227: *
228: * @return <code>null</code> if the task has not been completed
229: */
230: public Date getEndDate() {
231: return fEndDate;
232: }
233:
234: public List getHtmlParserMessages() {
235: return fHtmlParserMessages;
236: }
237:
238: /**
239: * Gets the location of the task
240: *
241: * @return <code>null</code> if unknown
242: */
243: public Location getLocation() {
244: return fLocation;
245: }
246:
247: /**
248: * @return the parent result, <code>null</code> for the top most result
249: */
250: StepResult getParent() {
251: return fParent;
252: }
253:
254: /**
255: * Gets the date at which the execution of the task started
256: *
257: * @return <code>null</code> if the task has not been started
258: */
259: public Date getStartDate() {
260: return fStartDate;
261: }
262:
263: /**
264: * Gets the description of the task
265: *
266: * @return <code>null</code> if unknown or no description set
267: */
268: public String getTaskDescription() {
269: return fTaskDescription;
270: }
271:
272: /**
273: * @return the task name.
274: */
275: public String getTaskName() {
276: return fTaskName;
277: }
278:
279: /**
280: * Indicates if the step has been executed (may be successfull or failed)
281: *
282: * @return <code>true</code> if executed
283: */
284: public boolean isCompleted() {
285: return getEndDate() != null;
286: }
287:
288: /**
289: * Indicates if the step has been successfully completed
290: *
291: * @return <code>true</code> if successful
292: */
293: public boolean isSuccessful() {
294: return fSuccessfull;
295: }
296:
297: /**
298: * Informs the result that the task if finished, allowing to stop the timer
299: *
300: * @param throwable the exception thrown by the task (if any)
301: * @param liHtmlParserMessages the list of html parser messages associated to the just finished task
302: */
303: void taskFinished(final Task task, final Throwable throwable,
304: final List liHtmlParserMessages) {
305: fEndDate = new Date();
306: fSuccessfull = throwable == null;
307: fHtmlParserMessages = liHtmlParserMessages;
308:
309: addNotExecutedChildren(task);
310: }
311:
312: /**
313: * Adds results of the step execution
314: *
315: * @param results
316: */
317: void addStepResults(final Map results) {
318: fAttributes.putAll(results);
319: }
320:
321: /**
322: * Gets the task attribute value
323: * @param key the attribute name (case insensitive)
324: * @return the value, <code>null</code> if task doesn't have this attribute
325: */
326: String getAttribute(final String key) {
327: if (key == null)
328: return null;
329:
330: for (final Iterator iter = fAttributes.entrySet().iterator(); iter
331: .hasNext();) {
332: final Map.Entry entry = (Map.Entry) iter.next();
333: if (key.equalsIgnoreCase((String) entry.getKey()))
334: return (String) entry.getValue();
335: }
336: return null;
337: }
338: }
|